#4306. 贪玩蓝月
题目描述
《贪玩蓝月》是目前最火爆的网页游戏。在游戏中每个角色都有若干装备,每件装备有一个特征值 $w$ 和一个战斗力 $v$ 。在每种特定的情况下,你都要选出特征值的和对 $p$ 取模后在一段范围内的装备,而角色死亡时自己的装备会爆掉。每个角色的物品槽可以看成一个双端队列,得到的装备会被放在两端,自己的装备爆掉也会在两端被爆。
现在我们有若干种事件和询问,如下所示:
- `IF w v`:在前端加入一件特征值为 $w$ 战斗力为 $v$ 的装备
- `IG w v`:在后端加入一件特征值为 $w$ 战斗力为 $v$ 的装备
- `DF`:删除最前端的装备
- `DG`:删除最后端的装备
- `QU l r`:在当前的装备中选取若干装备,他们的和对 $p$ 取模后在 $[l, r]$ 中,使得这些装备的战斗力之和最大
为了锻炼你的水平,请尽量使用在线做法。
题解
考虑只加不删的话,那就是普通的 $\text{dp}$ ,即 $f[i][j]$ 表示前 $i$ 个物品,特征值的和模 $p$ 为 $j$ 的最大战斗力和,然后背包转移即可
考虑有删数的话,就上线段树分治即可,于是就又是只有加数的了,效率: $O(mplogm)$
好像有种在线的做法but我不会
代码
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=2e5+5; int m,P,b[N],U[N],V[N],G[N]; LL f[30][500],F[500]; struct O{int x,y;}a[N]; vector<O>g[N];char ch[9]; #define Ls k<<1 #define Rs k<<1|1 #define mid ((l+r)>>1) void upd(int k,int l,int r,int L,int R,O v){ if (L<=l && r<=R){ g[k].push_back(v);return; } if (mid>=L) upd(Ls,l,mid,L,R,v); if (mid<R) upd(Rs,mid+1,r,L,R,v); } void qry(int k,int l,int r,int d){ int z=g[k].size(); for (int x,y,i=0;i<z;i++){ x=g[k][i].x,y=g[k][i].y; for (int j=0;j<P;j++) F[(j+x)%P]=f[d][j]+y; for (int j=0;j<P;j++) f[d][j]=max(f[d][j],F[j]); } if (l==r){ if (G[l]){ LL v=-1; for (int i=U[l];i<=V[l];i++) v=max(v,f[d][i]); printf("%lld\n",v); } return; } else{ for (int i=0;i<P;i++) f[d+1][i]=f[d][i]; qry(Ls,l,mid,d+1); for (int i=0;i<P;i++) f[d+1][i]=f[d][i]; qry(Rs,mid+1,r,d+1); } } int main(){ cin>>m;cin>>m>>P; int l=m+1,r=m; for (int i=1;i<=m;i++){ scanf("%s",ch); if (ch[0]=='I'){ if (ch[1]=='F') b[--l]=i, scanf("%d%d",&a[l].x,&a[l].y); else b[++r]=i, scanf("%d%d",&a[r].x,&a[r].y); } else if (ch[0]=='Q') G[i]=1,scanf("%d%d",&U[i],&V[i]); else{ if (ch[1]=='F') upd(1,1,m,b[l],i,a[l]),l++; else upd(1,1,m,b[r],i,a[r]),r--; } } for (int i=l;i<=r;i++) upd(1,1,m,b[i],m,a[i]); for (int i=1;i<P;i++) f[0][i]=-2e18; qry(1,1,m,0);return 0; }