[Codeforces Round #625][Codeforces 1320C/1321E. World of Darkraft: Battle for Azathoth]
题目链接:1320C - World of Darkraft: Battle for Azathoth/1321E - World of Darkraft: Battle for Azathoth
题目大意:有\(n\)种剑,每种有对应的攻击值和价格、\(m\)种盾,每种有对应的防御值和价格。以及\(p\)个怪兽,每个怪兽有对应的攻击值、防御值、打败它获得的金币数量。要求购买恰好一种剑、一种盾,求打败所有能打败的怪兽后的最大收益(可以为负),其中打败一个怪兽的充要条件是:剑的攻击值严格大于怪兽的防御值,且盾的防御值严格大于怪兽的攻击值。
题解:考虑枚举在买第\(i\)种剑的前提条件下的最大收益,这时只有防御值小于其攻击力的怪兽才有可能被打败。把这些怪兽放到一个集合内,就相当于有若干个怪兽,求购买一个盾牌的最优解(攻击力小于该盾牌防御值的怪兽的收益和 - 盾牌价格 最大)。
于是我们可以对剑的攻击值排序,这样枚举\(i\)的时候,满足条件的怪兽集合一定是单调上升的。每次将怪兽加入集合后,便可以更新所有防御值大于其攻击值的盾的收益。显然这个可以通过离散化盾的防御值用线段树的区间加以及查询全局最大值来求解,但由于本人实在太弱不怎么会写线段树,不得已使用了分块的做法。不过交上去后发现实际运行时间只有374ms,甚至比一部分的线段树做法跑得还快,故拿出来分享一下_(:з」∠)_
将盾按防御值排序后,将其分成若干块,设每块大小为\(B\),维护每块的最大收益,其中第\(i\)个盾的收益是\(f_i\),初始设为\(-cb_i\)。每次加入一个怪兽到集合里时,假设当前怪兽的攻击值是\(w\),奖励是\(v\),则所有满足\(b_i>w\)的\(f_i\)均要加上\(v\)。于是我们可以通过\(upper\text{_}bound\)找到第一个可以打败该怪兽的盾\(b_k\),进行修改操作。
对\(k\)所在的块,我们可以直接暴力修改\(f_k\)的值并更新所在块的答案。而对于后面每一个块的修改,我们可以通过打标记的方式,记录当前区域的收益被全体加上了多少,就不用修改块内的值了,之后求每个点的收益时只要额外加上标记的值就好。而每次修改时,我们可以实时更新当前的收益最大值,就不用额外进行询问操作了。
很明显当\(B\)取\(\sqrt{m}\)时最优,时间复杂度为\(O(p\sqrt{m})\)。
#include<bits/stdc++.h> using namespace std; #define N 200001 int n,m,p,B,nb,cur,ans,f[N],mx[N],bel[N],tag[N]; struct rua { int w,v,w2; void read(){scanf("%d%d",&w,&v);} void read2(){scanf("%d%d%d",&w,&w2,&v);} bool operator <(const rua &t)const{return w<t.w;} }a[N],b[N],c[N]; void Build() { int l=1,r=min(m,B); while(l<=m) { mx[++nb]=ans; for(int i=l;i<=r;i++) { bel[i]=nb; f[i]=-b[i].v; mx[nb]=max(mx[nb],f[i]); } cur=max(cur,mx[nb]); l=r+1,r=min(m,l+B-1); } } void add(int x) { int w=c[x].w2,v=c[x].v; int k=upper_bound(b+1,b+m+1,(rua){w,v,233})-b; while(k<=m && bel[k]==bel[k-1]) { f[k]+=v; mx[bel[k]]=max(mx[bel[k]],f[k]+tag[bel[k]]); cur=max(cur,mx[bel[k]]); k++; } if(k>m)return; for(int i=bel[k];i<=nb;i++) tag[i]+=v,mx[i]+=v,cur=max(cur,mx[i]); } int main() { B=sqrt(N)+1; cur=ans=-2147483647; scanf("%d%d%d",&n,&m,&p); for(int i=1;i<=n;i++)a[i].read(); for(int i=1;i<=m;i++)b[i].read(); for(int i=1;i<=p;i++)c[i].read2(); sort(a+1,a+n+1); sort(b+1,b+m+1); sort(c+1,c+p+1); Build(); int k=1; for(int i=1;i<=n;i++) { while(k<=p && c[k].w<a[i].w)add(k++); ans=max(ans,-a[i].v+cur); } printf("%d\n",ans); }
感觉自己老了...要是放在两年前有些两三四行东西是会被直接压成一行的_(:з」∠)_