[LOJ6515]「雅礼集训 2018 Day10」贪玩蓝月

本文介绍了一种使用双栈维护背包问题的解决方案,通过动态调整栈内元素实现高效查询,适用于处理大规模数据集的价值最大化问题。文章详细阐述了算法原理及其实现过程。

Description

要求维护一个双端队列,支持:
在队尾/队头添加一个体积为w,价值为v的物品
删除队尾/队头的物品
询问从所有物品中选出若干个,满足体积和对Mod取模后在[l,r]内的价值的最大值
Q<=50000,Mod<=500

Solution

考虑维护两个栈,每次在栈顶添加就直接背包
如果一个栈删空了就从另一个栈中取一半暴力重构
这个东西可以看做是线性的
询问就是一个区间最大值,用单调队列维护就好了
复杂度O(QMod)

Code

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define mp(a,b) make_pair(a,b)
using namespace std;

typedef long long ll;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

void write(ll x) {
	if (!x) {puts("0");return;}
	if (x<0) {x=-x;putchar('-');}
	char ch[20];int tot=0;
	for(;x;x/=10) ch[++tot]=x%10+'0';
	fd(i,tot,1) putchar(ch[i]);
	puts("");
}

const int N=5e4+5,M=505;
const ll inf=1e15;

int q,Mod;

struct Stack{
	int top;
	ll f[N][M];
	pair<int,int> a[N];
	void pre() {top=0;fo(i,1,Mod-1) f[0][i]=-inf;}
	void push(int w,int v) {
		w%=Mod;a[++top]=mp(w,v);
		fo(i,0,Mod-1) f[top][i]=max(f[top-1][i],f[top-1][(i-w+Mod)%Mod]+v);
	}
}L,R;

pair<int,int> d[N];

void rebuild() {
	int cnt=0;
	fd(i,L.top,1) d[++cnt]=L.a[i];
	fo(i,1,R.top) d[++cnt]=R.a[i];
	int pos=cnt/2;L.top=R.top=0;
	fd(i,pos,1) L.push(d[i].first,d[i].second);
	fo(i,pos+1,cnt) R.push(d[i].first,d[i].second);
}

int hd,tl,qd[M];

ll calc(int l,int r) {
	int len=r-l+1;hd=1;tl=0;
	ll ans=-1;
	fo(i,0,len-1) {
		while (hd<=tl&&L.f[L.top][qd[tl]]<=L.f[L.top][i]) tl--;
		qd[++tl]=i;
		ans=max(ans,R.f[R.top][(r-i+Mod)%Mod]+L.f[L.top][qd[hd]]);
	}
	fo(i,len,Mod-1) {
		while (hd<=tl&&L.f[L.top][qd[tl]]<=L.f[L.top][i]) tl--;
		while (hd<=tl&&qd[hd]<=i-len) hd++;
		qd[++tl]=i;
		ans=max(ans,R.f[R.top][(r-i+Mod)%Mod]+L.f[L.top][qd[hd]]);
	}
	fo(i,Mod-len,Mod-1) {
		while (hd<=tl&&qd[hd]<i) hd++;
		ans=max(ans,R.f[R.top][(l-i+Mod)%Mod]+L.f[L.top][qd[hd]]);
	}
	return ans;
}

char opt[5];

int main() {
	read();q=read();Mod=read();
	L.pre();R.pre();
	for(;q;q--) {
		scanf("%s",opt);
		if (opt[0]=='I'&&opt[1]=='F') {
			int w=read(),v=read();
			L.push(w,v);
		}
		if (opt[0]=='I'&&opt[1]=='G') {
			int w=read(),v=read();
			R.push(w,v);
		}
		if (opt[0]=='D'&&opt[1]=='F') {
			if (!L.top) rebuild();
			if (!L.top) R.top--;
			else L.top--;
		}
		if (opt[0]=='D'&&opt[1]=='G') {
			if (!R.top) rebuild();
			if (!R.top) L.top--;
			else R.top--;
		}
		if (opt[0]=='Q') {
			int l=read(),r=read();
			write(calc(l,r));
		}
	}
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值