[BZOJ4700]适者

[BZOJ4700]适者

题目大意:

(n(nle3 imes10^5))个敌人,每个敌人有一个攻击力(v_i)和一个防御力(d_i)。你的攻击力是(a)。战斗看做回合制,每回合进程如下:

  1. 你选择某个敌人进行攻击,令其防御力减少(a),若防御力(<0)则该敌人被击败。
  2. 所有存活的敌人每人对你造成(v_i)点损失。

战斗开始前你有机会直接去掉对方的两个敌人。

你拥有无限的血量,请最小化你的损失。

思路:

首先不考虑去掉两个敌人的情况。

显然我们可以按一定顺序依次击败各个敌人,而不必一个人打一半就去打其他人。

(t_i)表示击败敌人(i)所花的时间,则(t_i=lceilfrac{d_i}a ceil)

考虑攻击的顺序。(i)(j)前面,当且仅当(t_i imes v_j<t_j imes v_i)

此时(ans=sum_{i=1}^n(sum_{j=1}^{i}t_j-1) imes v_i)

现在考虑事先去掉两个敌人的情况,即如何选择去掉的敌人使损失最小。

(pre)表示(t_i)的前缀和,用(suf)表示(v_i)的后缀和。

考虑只去掉一个敌人的情况,去掉(i)这个敌人可以使答案减少(c_i=suf[i] imes t_i+(pre[i-1]-1) imes v_i)

而去掉两个人就是要找到一对(i,j),使得(c_i+c_j-v_i imes t_j)最大。

考虑固定一个(i),则(j)的贡献就是一个关于(v_i)的一次函数,用李超树或CHT维护凸壳即可。

时间复杂度(mathcal O(nlog n))

源代码:

#include<cmath>
#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
typedef long long int64;
const int N=3e5+2,LIM=1e4;
struct Role {
	int v,t;
	bool operator < (const Role &rhs) const {
		return t*rhs.v<rhs.t*v;
	}
};
Role r[N];
int64 pre[N],suf[N];
class SegmentTree {
	#define _left <<1
	#define _right <<1|1
	#define mid ((b+e)>>1)
	private:
		struct Node {
			int64 a,b;
			int left,right;
		};
		Node node[N<<2];
	public:
		void insert(const int &p,const int &b,const int &e,int64 i,int64 j) {
			if(node[p].a==0&&node[p].b==0) {
				node[p].a=i;
				node[p].b=j;
				return;
			}
			const int64 lval1=i*b+j,rval1=i*e+j;
			const int64 lval2=node[p].a*b+node[p].b;
			const int64 rval2=node[p].a*e+node[p].b;
			if(lval1>=lval2&&rval1>=rval2) {
				node[p].a=i;
				node[p].b=j;
				return;
			}
			if(b==e) return;
			const double c=1.*(node[p].b-j)/(i-node[p].a);
			if(c<mid&&rval1>rval2) {
				insert(p _left,b,mid,node[p].a,node[p].b);
				node[p].a=i;
				node[p].b=j;
				return;
			}
			if(c<mid&&rval1<=rval2) {
				insert(p _left,b,mid,i,j);
				return;
			}
			if(c>=mid&&lval1>lval2) {
				insert(p _right,mid+1,e,node[p].a,node[p].b);
				node[p].a=i;
				node[p].b=j;
				return;
			}
			if(c<mid&&lval1<=lval2) {
				insert(p _right,mid+1,e,i,j);
				return;
			}
		}
		int64 query(const int &p,const int &b,const int &e,const int &x) {
			if(node[p].a==0&&node[p].b==0) return 0;
			int64 ret=node[p].a*x+node[p].b;
			if(b==e) return ret;
			if(x<=mid) ret=std::max(ret,query(p _left,b,mid,x));
			if(x>mid) ret=std::max(ret,query(p _right,mid+1,e,x));
			return ret;
		}
	#undef _left
	#undef _right
	#undef mid
};
SegmentTree t;
int main() {
	const int n=getint(),atk=getint();
	for(register int i=1;i<=n;i++) {
		const int a=getint(),d=getint();
		const int t=ceil(1.*d/atk);
		r[i]=(Role){a,t};
	}
	std::sort(&r[1],&r[n]+1);
	for(register int i=1;i<=n;i++) pre[i]=pre[i-1]+r[i].t;
	for(register int i=n;i>=1;i--) suf[i]=suf[i+1]+r[i].v;
	int64 ans=0;
	for(register int i=1;i<=n;i++) {
		const int64 tmp=suf[i]*r[i].t+(pre[i-1]-1)*r[i].v;
		if(i>1) ans=std::max(ans,tmp+t.query(1,1,LIM,r[i].v));
		t.insert(1,1,LIM,-r[i].t,tmp);
	}
	ans=-ans;
	for(register int i=1;i<=n;i++) {
		ans+=(pre[i]-1)*r[i].v;
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/skylee03/p/9338217.html