题目
题目链接:https://www.ybtoj.com.cn/contest/113/problem/3
(nleq 3 imes 10^5,A,D,ATKleq 10^4)。
思路
首先直接令 (b_i=lceilfrac{d_i}{ATK}
ceil)。
不难发现如果选择打一个机器人,那么一定会把它打到死。不然先打下一个打死的会更优。
那么问题转化为有一个长度为 (n) 的排列 (p),我们需要最小化
[sum^{n}_{i=1}a_i imes ((sum_{j=1}^{i}b_i)-1)
]
这个东西跟国王游戏类似,考虑调整法:对于最终序列相邻的两项 (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) 两个机器人,代价就是
[(sum^{n}_{i=1}f_i)-f_x-g_x-f_y-g_y+b_xa_y
]
我们只需要最小化上面这个东西。
考虑枚举 (x),那么等价于最大化 (f_y+g_y-b_xa_y)。我们可以把这个东西看成一个关于 (b_x) 的一次函数,那么其实就是李超树的板子。直接维护区间直线“最大值位置最多”的直线即可。
时间复杂度 (O(nlog 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;
}