Loading

题解 Codeforces 1286E Fedya the Potter Strikes Back

题意

对于一个第 \(i\) 个位置额外带一个权值 \(w_i\) 的字符串 \(S_0\),定义 \(S_0[l,r]\) 的权值为:如果 \(S_0[l,r]=S_0[1,r-l+1]\),则权值为 \(\min\limits_{l \leq k \leq r}\{w_k\}\),否则为 \(0\)

现在你有一个初始为空的字符串 \(S\),有 \(n\) 次操作,第 \(i\) 次往 \(S\) 后插入一个字符 \(c_i\) 并给定该位置的权值 \(w_i\)。每次操作后,你需要求出 \(S\) 所有子串的权值和。强制在线

\(1 \leq n \leq 6 \times 10^5,0 \leq w_i < 2^{30}\)

题解

注意到新加入一个字符不会影响原来子串的权值。对于第 \(i\) 次操作,我们只需要考虑 \(S[1,i]\) 有多少后缀是合法的,并计算这些后缀的权值。

首先,\(S[1,i]\) 肯定是合法的。观察到剩下的合法后缀一定是 \(S[1,i]\) 的 border。除了 \(S[1,1]\)(当然它甚至有可能不是 \(S[1,i]\) border),剩下的 border 都是 \(S[1,i-1]\) 的 border 加上一位变过来的。因此我们可以维护 \(S[1,i-1]\) 的所有合法后缀集合和 \(S[1,i-1]\) 所有合法后缀的权值组成的可重集,然后适当地删除。

删除的时候,把合法后缀集合中下一位和 \(c_i\) 不相等的后缀的权值拎出来,在可重集中删掉即可。我们发现删除的复杂度是假的,因为待删除的位置不太连续,会导致大量遍历但不出现删除的情况。于是我们额外维护 \(nex(i,j)\),表示 \(i\) 的 border 中最长的下一位为 \(j\) 的 border 的位置。那么不合法的后缀就是遍历所有 \(j \neq c_i\)\(nex(i-1,j),nex(nex(i-1,j),j),\cdots\),这些 border 对应的后缀,把对应权值删除。

我们发现目前可重集中的对应权值仍然是 \(i-1\) 时的答案。考虑 \(i\) 的影响,会发现影响就是原来所有大于 \(w\) 的权值都变成了 \(w\)。暴力修改即可。

可重集用 map 维护,计算权值可以用线段树或者单调栈(这个没细想过,不知道行不行)。

注意爆 long long 了,当然可以 __int128 水过去。

# include <bits/stdc++.h>
// # define int long long
const int N=600010,INF=0x3f3f3f3f;

typedef long long ll;
int nex[N][26],fail[N],n,w[N]; // nex[i,x] = pos 表示 [1,pos] 是 [1,i] 的 border 且 s[pos+1] = x 
int s[N];
__int128 ans;
ll curv; // 记录下 border 的和(即不包含 [1,x] 的所有相等前缀) 
int minx[N<<2];

std::map <int,int> S;

inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
inline void print(__int128 x){
	if(x<0) putchar('-'),x-=x;
	if(x>9) print(x/10);
	putchar(x%10+'0');
	return; 
}
inline int lc(int x){
	return x<<1;
}
inline int rc(int x){
	return x<<1|1;
}
inline void pushup(int k){
	minx[k]=std::min(minx[lc(k)],minx[rc(k)]);
	return;
}
void change(int k,int l,int r,int x,int v){
	if(l==r) return minx[k]=v,void();
	int mid=(l+r)>>1;
	if(x<=mid) change(lc(k),l,mid,x,v);
	else change(rc(k),mid+1,r,x,v);
	pushup(k);
	return;
}
int query(int k,int l,int r,int L,int R){
	if(L<=l&&r<=R) return minx[k];
	int mid=(l+r)>>1,res=2e9;
	if(L<=mid) res=std::min(res,query(lc(k),l,mid,L,R));
	if(mid<R) res=std::min(res,query(rc(k),mid+1,r,L,R));
	return res;
}


signed main(void){
	n=read();
	char in[2];
	scanf("%s",in);
	s[1]=in[0]-'a',printf("%lld\n",w[1]=read()),ans+=w[1],change(1,1,n,1,w[1]);
	for(int i=2,val,j=0;i<=n;++i){
		scanf("%s",in),val=read();
		s[i]=(in[0]-'a'+ans%26)%26,w[i]=val^(ans%(1<<30));
		change(1,1,n,i,w[i]);
		ans+=query(1,1,n,1,i);
		while(j&&s[j+1]!=s[i]) j=fail[j];
		if(s[j+1]==s[i]) ++j;
		fail[i]=j;
		for(int k=0;k<26;++k) nex[i][k]=nex[fail[i]][k];
		nex[i][s[fail[i]+1]]=fail[i]; // i 比 fail[i] 新增的 border 就是 fail[i]
		for(int k=0;k<26;++k){
			if(k!=s[i]){
				for(int j=nex[i-1][k];j;j=nex[j][k]){
					int res=query(1,1,n,(i-1)-j+1,i-1); // 长度为 j 的 border 对应 [i-j,i-1] 这个后缀
					curv-=res,--S[res]; // 从 map 中移除
				}
			}
		}
		std::vector <int> delv; // 记录下移除的权值集合
		for(std::map <int,int>::iterator it=S.upper_bound(w[i]);it!=S.end();++it){
			std::pair <int,int> now=*it;
			curv+=1ll*(w[i]-now.first)*now.second,S[w[i]]+=now.second,delv.push_back(now.first);
		}
		for(auto v:delv) S.erase(v);
		if(s[i]==s[1]) curv+=w[i],++S[w[i]];
		ans+=curv,print(ans),puts("");
	}
	return 0;
}

posted @ 2022-07-07 17:11  Meatherm  阅读(29)  评论(0编辑  收藏  举报