#4306. 贪玩蓝月

题目描述

《贪玩蓝月》是目前最火爆的网页游戏。在游戏中每个角色都有若干装备,每件装备有一个特征值 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值