zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    Python下载安装
    批量修改样式及全选反选
    小99
    练习题
    练习
    对象、函数
    操作document对象练习
    练习题
    0513-2
    0513-1
  • 原文地址:https://www.cnblogs.com/skylee03/p/9338217.html
Copyright © 2011-2022 走看看