【雅礼集训 2018 Day10】【栈】【背包dp】【均摊分析】贪玩蓝月

0 篇文章 0 订阅

【描述】
现在我们有若干种事件和询问,如下所示:
IF w v:在前端加入一件特征值为 w 战斗力为 v 的装备
IG w v:在后端加入一件特征值为 w 战斗力为 v 的装备
DF:删除最前端的装备
DG:删除最后端的装备
QU l r:在当前的装备中选取若干装备,他们的和对 p 取模后在 [l,r] 中,使得这些装备的战斗力之和最大

【思路】

如果只有一边插入删除,那么我们可以用一个栈来维护这个背包dp。可是我们要支持在两端操作,所以我们可以维护两个栈。
1.插入:根据方向直接在第一个栈或第二个栈插入
2.删除:在第一个栈或第二个栈删除,如果有一个栈空了,我们暴力把另一个栈的取出来,两个栈平均分配一下物品,重新暴力插入两个栈即可。注意特判只有一个物品的情况。
3.询问:我们需要合并两个栈的dp信息,这可以使用单调队列实现。
除此之外,这道题有线段树分治做法。
代码:

#include<bits/stdc++.h>
#define re register
#define mp make_pair
using namespace std;
const int M=5e2+5,N=2.5e4+5;
inline int red(){
    int data=0;bool w=0; char ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return w?-data:data;
}int n,m,c,mod;
typedef long long ll;
typedef pair<int,int>T;
inline int id(int x){
	if(x<0)x+=mod;
	else if(x>=mod)x-=mod;
	return x;
}
inline ll max(const ll&a,const ll&b){return a>b?a:b;}
struct stk{
	T a[N];int top;
	ll dp[N][M];
	void prework(){top=0;for(int re i=1;i<mod;i++)dp[0][i]=-1e16;dp[0][0]=0;}
	inline void push(T x){
		int w=x.first%mod,v=x.second;
		a[++top]=mp(w,v);
		for(int re i=0;i<mod;++i)
			dp[top][i]=max(dp[top-1][i],dp[top-1][id(i-w)]+v);
	}
}s1,s2;
T rb[N];
inline void rebuild(){
	int cnt=0;
	for(int re i=s1.top;i>0;--i)rb[++cnt]=s1.a[i];
	for(int re i=1;i<=s2.top;++i)rb[++cnt]=s2.a[i];
	int cnt1=cnt>>1;s1.top=0,s2.top=0;
	for(int re i=cnt1;i>0;i--)s1.push(rb[i]);
	for(int re i=cnt1+1;i<=cnt;++i)s2.push(rb[i]);
}
int q[N],L=1,R=0;
ll *a,*b;
inline void cmax(ll&x,const ll&y){(x<y)&&(x=y);}
inline ll query(int l,int r){
	L=1;R=0;
	int len=r-l+1;ll ans=-1;
	a=s1.dp[s1.top];b=s2.dp[s2.top];
	for(int re i=0;i<len;++i){
		while(L<=R&&a[i]>=a[q[R]])R--;
		q[++R]=i;cmax(ans,a[q[L]]+b[id(r-i)]);
	}for(int re i=len;i<mod;++i){
		while(L<=R&&q[L]<=i-len)L++;
		while(L<=R&&a[i]>=a[q[R]])R--;
		q[++R]=i;cmax(ans,a[q[L]]+b[id(r-i)]);
	}for(int re i=mod-len;i<mod;++i){
		while(L<=R&&q[L]<i)L++;
		cmax(ans,a[q[L]]+b[id(l-i)]);
	}return ans;
}
char s[5];
int main()
{
	red();m=red();mod=red();
	s1.prework();s2.prework();
	while(m--){
		scanf("%s",s);
		switch(s[0]){
			case'I':{
				int a=red(),b=red();
				switch(s[1]){
					case'F':{s1.push(mp(a,b));break;} 
					case'G':{s2.push(mp(a,b));break;}
					break;
				}break;
			}
			case'D':{
				switch(s[1]){
					case'F':{if(!s1.top)rebuild();s1.top?s1.top--:s2.top--;break;}
					case'G':{if(!s2.top)rebuild();s2.top?s2.top--:s1.top--;break;}
				}break;
			}
			case'Q':{
				int a=red(),b=red();
				cout<<query(a,b)<<"\n";
				break;
			}
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值