题目描述
《贪玩蓝月》是目前最火爆的网页游戏。在游戏中每个角色都有若干装备,每件装备有一个特征值 w w w 和一个战斗力 v v v 。在每种特定的情况下,你都要选出特征值的和对 p p p 取模后在一段范围内的装备,而角色死亡时自己的装备会爆掉。每个角色的物品槽可以看成一个双端队列,得到的装备会被放在两端,自己的装备爆掉也会在两端被爆。
现在我们有若干种事件和询问,如下所示:
IF w v
:在前端加入一件特征值为 w w w 战斗力为 v v v 的装备IG w v
:在后端加入一件特征值为 w w w 战斗力为 v v v 的装备DF
:删除最前端的装备DG
:删除最后端的装备QU l r
:在当前的装备中选取若干装备,他们的和对 p p p 取模后在 [ l , r ] [l, r] [l,r] 中,使得这些装备的战斗力之和最大
为了锻炼你的水平,请尽量使用在线做法。
题解
考虑只加不删的话,那就是普通的 dp \text{dp} dp ,即 f [ i ] [ j ] f[i][j] f[i][j] 表示前 i i i 个物品,特征值的和模 p p p 为 j j j 的最大战斗力和,然后背包转移即可
考虑有删数的话,就上线段树分治即可,于是就又是只有加数的了,效率: O ( m p l o g m ) O(mplogm) 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;
}