【YbtOJ#643】机器决斗
作者:互联网
题目
题目链接:https://www.ybtoj.com.cn/contest/113/problem/3
\(n\leq 3\times 10^5,A,D,ATK\leq 10^4\)。
思路
首先直接令 \(b_i=\lceil\frac{d_i}{ATK}\rceil\)。
不难发现如果选择打一个机器人,那么一定会把它打到死。不然先打下一个打死的会更优。
那么问题转化为有一个长度为 \(n\) 的排列 \(p\),我们需要最小化
这个东西跟国王游戏类似,考虑调整法:对于最终序列相邻的两项 \(i,j\),交换他们的代价是 \(a_ib_j-a_jb_i\),也就是说,最终相邻两项必然有 \(a_ib_j>a_jb_i\)。直接排序就可以了。
接下来考虑删除两个机器人后的最小代价。记 \(f_i=a_i(\sum^{i}_{j=1}b_j)-1,g_i=b_i(\sum_{j=i+1}^{n}a_j)\),假设我们删除 \(x,y\) 两个机器人,代价就是
我们只需要最小化上面这个东西。
考虑枚举 \(x\),那么等价于最大化 \(f_y+g_y-b_xa_y\)。我们可以把这个东西看成一个关于 \(b_x\) 的一次函数,那么其实就是李超树的板子。直接维护区间直线“最大值位置最多”的直线即可。
时间复杂度 \(O(n\log n)\)。
除此之外,还可以用平衡树或者 CDQ 分治维护上凸壳。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=300010;
int rt,n,m;
ll ans,sum,f[N],g[N];
struct node
{
int a,b;
}a[N];
bool cmp(node x,node y)
{
return x.b*y.a<x.a*y.b;
}
bool check(int x,int i,int j)
{
return f[j]+g[j]-a[j].a*x<f[i]+g[i]-a[i].a*x;
}
struct SegTree
{
int tot,lc[N*4],rc[N*4],res[N*4];
int update(int x,int l,int r,int i)
{
if (!x) x=++tot;
int j=res[x];
if (l==r)
{
if (!j || check(l,i,j)) res[x]=i;
return x;
}
if (!j || (check(l,i,j) && check(r,i,j))) return (res[x]=i),x;
if (!check(l,i,j) && !check(r,i,j)) return x;
int mid=(l+r)>>1;
if (f[i]+g[i]>f[j]+g[j])
{
if (check(mid,i,j))
rc[x]=update(rc[x],mid+1,r,j),res[x]=i;
else
lc[x]=update(lc[x],l,mid,i);
}
else
{
if (check(mid,i,j))
lc[x]=update(lc[x],l,mid,j),res[x]=i;
else
rc[x]=update(rc[x],mid+1,r,i);
}
return x;
}
int query(int x,int l,int r,int k)
{
if (!x) return 0;
if (l==r) return res[x];
int mid=(l+r)>>1,p;
if (k<=mid) p=query(lc[x],l,mid,k);
else p=query(rc[x],mid+1,r,k);
if (!p || check(k,res[x],p)) return res[x];
return p;
}
}seg;
int main()
{
freopen("fittest.in","r",stdin);
freopen("fittest.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].a,&a[i].b);
a[i].b=(a[i].b-1)/m+1;
}
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
ans+=a[i].b;
f[i]=(ans-1)*a[i].a;
sum+=f[i];
}
ans=0;
for (int i=n;i>=1;i--)
{
g[i]=ans*a[i].b;
ans+=a[i].a;
}
ans=sum;
for (int i=n-1;i>=1;i--)
{
rt=seg.update(rt,1,1e4,i+1);
int j=seg.query(rt,1,1e4,a[i].b);
ans=min(ans,sum-f[i]-g[i]-f[j]-g[j]+a[i].b*a[j].a);
}
printf("%lld",ans);
return 0;
}
标签:return,int,YbtOJ,sum,决斗,mid,update,643,ans 来源: https://www.cnblogs.com/stoorz/p/14402069.html