【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\),我们需要最小化

\[\sum^{n}_{i=1}a_i\times ((\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(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;
}
posted @ 2021-02-14 15:53  stoorz  阅读(60)  评论(0编辑  收藏  举报