题解 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;
}