把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CCPC-Wannafly Winter Camp Day4 (Div1) I】咆咆咆哮(三分+贪心)

点此看题面

大致题意:\(n\)张卡牌,每张卡牌有两种用法:使场上增加一个伤害为\(a_i\)的生物,或使场上所有生物伤害增加\(b_i\)。求最大总伤害。

三分

我们可以三分使用\(a_i\)的卡牌张数

证明如下:

假设使用\(a_i\)的卡牌张数最优为\(x\),此时选用的\(b_i\)总和为\(t\)

  • 证明:从最优决策点向左总伤害值递减

    此时如果将一张选\(a_i\)的卡牌改为选\(b_i\),则伤害变化值应为\(b_i*(x-1)-a_i-t\)

    因为原先状态为最优状态,所以该操作肯定不能使答案更优,也就是说:\(b_i*(x-1)-a_i-t\le0\)

    再考虑若当前使用\(a_i\)的卡牌张数是\(y(y<x)\),选用的\(b_i\)总和为\(t'\),则伤害变化值应为\(b_i*(y-1)-a_i-t'\)

    由于\(y<x\)\(b_i>0\),所以\(b_i*(y-1)<b_i*(x-1)\)

    由于使用\(a_i\)的卡牌张数减少,所以使用\(b_i\)的卡牌张数增加,因此\(t'>t\)

    \(a_i\)是不变的,且\(b_i*(x-1)-a_i-t\le0\),所以\(b_i*(y-1)-a_i-t'\)必然小于\(0\)

    也就是说伤害变化值为负数,肯定是不优的。

    可见从最优决策点向左总伤害值递减

  • 证明:从最优决策点向右总伤害值递减

    此时如果将一张选\(b_i\)的卡牌改为选\(a_i\),则伤害变化值应为\(a_i+t-b_i*(x-1)\)

    因为原先状态为最优状态,所以该操作肯定不能使答案更优,也就是说:\(a_i+t-b_i*(x-1)\le0\)

    再考虑若当前使用\(a_i\)的卡牌张数是\(y(y>x)\),选用的\(b_i\)总和为\(t'\),则伤害变化值应为\(a_i+t'-b_i*(y-1)\)

    由于\(y>x\)\(b_i>0\),所以\(b_i*(y-1)>b_i*(x-1)\)

    由于使用\(a_i\)的卡牌张数增加,所以使用\(b_i\)的卡牌张数减少,因此\(t'<t\)

    \(a_i\)是不变的,且\(a_i+t-b_i*(x-1)\le0\),所以\(a_i+t'-b_i*(y-1)\)必然小于\(0\)

    也就是说伤害变化值为负数,肯定是不优的。

    可见从最优决策点向右总伤害值递减

综上所述,这是一个单峰函数,具备可三分性。

贪心求出最大伤害

上面我们已经证明了这道题有可三分性,那对于确定的使用\(a_i\)的卡牌张数,我们该如何求出最大伤害呢?

这可以用贪心

假设确定使用\(a_i\)\(x\)张,则我们先定义一个\(res\)统计答案,初始化它为\(\sum_{i=1}^na_i\)

由于使用\(a_i\)的卡牌张数是确定的,则对于第\(i\)张牌,它使用\(b_i\)能造成的伤害为\(b_i*x\)

然后,我们用它使用\(b_i\)能造成的伤害,减去它使用\(a_i\)能造成的伤害,就得到了\(b_i*x-a_i\)

显然,我们可以将这进行排序,然后选择最大的\(n-x\)个与\(res\)相加,这样就能清除原先选择\(a_i\)\(res\)造成的贡献,并加上选择\(b_i\)\(res\)造成的贡献。

具体实现详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define LL long long
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,a[N+5],b[N+5];LL s[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define tn (x<<3)+(x<<1)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
I LL GetAns(CI x)//求出在使用a[i]的卡牌张数为x时的最优答案
{
	RI i;Reg LL res=0;for(i=1;i<=n;++i) res+=a[i],s[i]=1LL*b[i]*x-a[i];//初始化答案
	for(sort(s+1,s+n+1),i=n;i^x;--i) res+=s[i];return res;//排序+贪心,返回答案
}
I LL Solve(RI l,RI r)//三分
{
	RI i,mid1,mid2;Reg LL res=0,t;
	W(r-l>2) mid1=l+(r-l)/3,mid2=r-(r-l)/3,GetAns(mid1)>GetAns(mid2)?r=mid2:l=mid1;//三分
	for(i=l;i<=r;++i) t=GetAns(i),Gmax(res,t);return res;//求答案
}
int main()
{
	RI i;for(F.read(n),i=1;i<=n;++i) F.read(a[i],b[i]);//读入数据
	return printf("%lld",Solve(1,n)),0;//输出答案
}
posted @ 2019-03-09 15:20  TheLostWeak  阅读(410)  评论(0编辑  收藏  举报