BZOJ4700

4700: 适者

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 43   Solved: 21
[ Submit][ Status][ Discuss]

Description

【题目背景】
“虽然不知道那两台是谁干掉的,不过任务完成了。”一一次祖伽密.
【题意描述】
敌方有n台人形兵器,每台的攻击力为Ai,护甲值为Di。我方只有一台人形兵器,攻击力为ATK。战斗看作回合制,
每回合进程如下:
  ·1 我方选择对方某台人形兵器并攻击,令其护甲值减少ATK,
若护甲值<0则被破坏。
  ·2 敌方每台未被破坏的人形兵器攻击我方基地造成Ai点损失。
但是,在第一回合开始之前,某两台敌方的人形兵器被干掉了(秒杀)。问最好情况下,我方基地会受到多少点损
失。

Input

第一行两个数n,ATK,表示敌方人形兵器数量和我方人形兵器攻击力。
接下来n行,每行两个数A,Di,表示对方第i台人形兵器的攻击力和护甲值。
3<=n<=3×10^5,Ai,Di<=10^4,ATK<10^4

Output

只一行,一个数,表示最好情况下我方基地会受到的损失总和。

Sample Input

3 7
30 8
7 35
1 209

Sample Output

28
【样例说明】
最好情况下,被秒杀的是敌方1、3号人形兵器,接下来需要5回合解决对方的2号人形兵器,对方共攻击4次,总计
造成28点伤害。可以证明没有更优的情况。


这个题好气啊...

di=(D-1)/ATK+1,Pi=d1+d2+...+di,Ti=A1+A2+...+Ai

先考虑没有秒杀的情况,就按ai/di降序排序即可

假设在这个排好序上秒杀i,j(j<i),则ans-=aj*(Pj-1)+(Tn-Tj)*dj+ai*(Pi-1)*(Tn-Ti)*di-ai*dj

令Ki=ai*(Pi-1)+(Tn-Ti)*di,则求max{Ki+Kj-ai*dj}

是不是有点像斜率优化?

fi=Ki+Kj-ai*dj

ai*dj+fi-Ki=Kj 要使fi-Ki最大

ai,dj不递增?cdq分治即可

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 300005
using namespace std;
typedef long long ll;
int bh[maxn],q[maxn],bhd[maxn],bha[maxn];
int A[maxn],d[maxn],P[maxn],T[maxn],n,atk;
ll ans,pr,K[maxn];

int comp1(const int x,const int y){return A[x]*d[y]>A[y]*d[x];}
bool inline fc(int x,int y,int z){
	ll x1=d[x], y1=K[x], x2=d[y], y2=K[y], x3=d[z], y3=K[z];
	return (y1-y2)*(x2-x3)>=(y2-y3)*(x1-x2);
}
void cdq(int xl,int xr){
	if(xl==xr)return ; 
	int mid=xl+xr>>1;
	cdq(xl,mid);
	cdq(mid+1,xr);
	int l=1,r=0;
	for(register int i=xl;i<=mid;++i){
		while(l<r&&fc(bhd[i],q[r],q[r-1]))r--;
		q[++r]=bhd[i];
	}
	for(register int i=mid+1;i<=xr;++i){
		while(l<r&&K[q[l]]-K[q[l+1]]<=(ll)A[bha[i]]*(d[q[l]]-d[q[l+1]]))l++;
		if(l<=r)ans=max(ans,K[bha[i]]+K[q[l]]-(ll)A[bha[i]]*d[q[l]]);
	}
	register int i=xl,j=mid+1,ptr=xl;
	for(;i<=mid&&j<=xr;){
		if(d[bhd[i]]<d[bhd[j]])bh[ptr++]=bhd[i++];
		else bh[ptr++]=bhd[j++];
	}
	for(;i<=mid;)bh[ptr++]=bhd[i++];
	for(;j<=xr;)bh[ptr++]=bhd[j++];
	for(int i=xl;i<=xr;++i)bhd[i]=bh[i];
	i=xl,j=mid+1,ptr=xl;
	for(;i<=mid&&j<=xr;){
		if(d[bha[i]]<d[bha[j]])bh[ptr++]=bha[i++];
		else bh[ptr++]=bha[j++];
	}
	for(;i<=mid;)bh[ptr++]=bha[i++];
	for(;j<=xr;)bh[ptr++]=bha[j++];
	for(int i=xl;i<=xr;++i)bha[i]=bh[i];
}
int main(){
	scanf("%d%d",&n,&atk);
	for(register int i=1;i<=n;++i)bh[i]=i;
	for(register int i=1;i<=n;++i){
		scanf("%d%d",A+i,d+i);
		d[i]=(d[i]-1)/atk+1;
	}
	sort(bh+1,bh+n+1,comp1);
	for(register int i=1;i<=n;++i){
		P[i]=P[i-1]+d[bh[i]];
		T[i]=T[i-1]+A[bh[i]];
		bhd[i]=bha[i]=bh[i];
	}
	for(register int i=1;i<=n;++i){
		K[bh[i]]=(ll)A[bh[i]]*(P[i]-1)+(ll)(T[n]-T[i])*d[bh[i]];
		pr+=(ll)A[bh[i]]*(P[i]-1);
	}
	cdq(1,n);
	printf("%lld",pr-ans);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值