\(\text{Description}\)

传送门

\(\text{Solution}\)

你会发现题目就是让你维护一个 "可撤销" 的 \(01\) 背包。不过背包应该是不能撤销的。也有可能是我太鶸了

我们可以根本不撤销 —— 线段树分治。只需要算出每件装备的存在区间,插入线段树上即可。每件装备只用插入 \(\mathcal O(\log m)\) 次,单次查询是 \(\mathcal O(p)\) 的。总共是 \(\mathcal O(pm\log m)\) 的。听上去好像挺卡的

或者,选用一个撤销代价不那么大的做法。

考虑用两个栈维护两端的插入。

插入时将新装备加入对应栈的背包。删除就直接删。如果一个栈删完了就需要将另一个栈内元素分成两半,再重新插入和计算背包。

单次查询可以用 \(\rm st\) 表将两个栈拼起来,是 \(\mathcal O(p\log p)\) 的。重要的是分析删除的复杂度:令势能函数为 \(\Phi=|\text{size}_1-\text{size}_2|\),对于一次 \(\mathcal O(\log p)\) 的插入操作,势能至多增加 \(1\);而对于一次重构,势能降为零,且重构复杂度显然是 \(\mathcal O(\Phi\log p)\).

\(\text{Code}\)

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

#include <cstring>
#include <iostream>
using namespace std;

typedef long long ll;

const int maxm=5e4+5,maxp=505;

int m,mod,tp[2],a,b,lg[maxp];
char op[5];
ll f[2][maxm][maxp],st[maxp][11],ans;
struct node {
	int w,v;
} sta[2][maxm];

ll Ask(int l,int r) {
	int len=lg[r-l+1];
	return max(st[l][len],st[r-(1<<len)+1][len]);
}

void Query() {
	int l,r;
	a=read(9),b=read(9); ans=-1;
	rep(i,0,mod-1) st[i][0]=f[1][tp[1]][i];
	rep(j,1,9) for(int i=0;i+(1<<j)-1<mod;++i) st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
	rep(i,0,mod-1) 
		if(f[0][tp[0]][i]>=0) {
			l=a-i,r=b-i;
			if(l<0) l+=mod;
			if(r<0) r+=mod;
			if(l<=r) ans=max(ans,f[0][tp[0]][i]+Ask(l,r));
			else ans=max(ans,f[0][tp[0]][i]+max(Ask(l,mod-1),Ask(0,r)));
		}
	print(ans,'\n');
}

void Insert(bool x,int a,int b) {
	sta[x][++tp[x]]=(node){a,b};
	int j;
	rep(i,0,mod-1) {
		j=(i+a>=mod?i+a-mod:i+a);
		f[x][tp[x]][j]=max(f[x][tp[x]-1][j],f[x][tp[x]-1][i]+b);
	}
}

void Delete(bool x) {
	if(tp[x]) return (void)(--tp[x]);
	int mid=tp[x^1]+1>>1;
	// 此处 mid 取大于等于 1/2,是为了保证至少拿出一件物品
	fep(i,mid,2) Insert(x,sta[x^1][i].w,sta[x^1][i].v);
	int tmp=tp[x^1]; tp[x^1]=0;
	rep(i,mid+1,tmp) Insert(x^1,sta[x^1][i].w,sta[x^1][i].v);
}

int main() {
	read(9),m=read(9),mod=read(9); 
	memset(f,192,sizeof f);
	f[0][0][0]=f[1][0][0]=0;
	rep(i,2,mod) lg[i]=lg[i>>1]+1;
	while(m--) {
		scanf("%s",op);
		if(op[0]=='I') a=read(9)%mod,b=read(9),Insert(op[1]=='G',a,b);
		else if(op[0]=='D') Delete(op[1]=='G');
		else Query();
	}
	return 0;
}

\(\text{I love Wordle!!!}\)

见缝插针

I guessed this 5-letter word in 3/6 tries.

⬛🟩🟩⬛⬛
⬛🟩🟩⬛🟩
🟩🟩🟩🟩🟩

Can you guess this word?

posted on 2022-03-31 20:40  Oxide  阅读(77)  评论(0编辑  收藏  举报