题解 P6385 【『MdOI R2』Little Goth】
EternalAlexander · · 题解
终于有一个不是 Ynoi 的月赛 F 题了,是不是很感动
若无特别说明,
过于显然的结论不说明了,需要详细题解请报讲评
结论 -1
在所有合法的
结论 0
任何时候,假如满足
结论 1
假如存在一种方案,使得在进行第一次操作三时,操作串不是给定的
证明 1
注意到,在这种方案中,你一定在第一次操作三之前使用了
假如你使用了上述操作,并且在第一次操作三时的
因为这样进行操作三时的操作串是原来的操作串的子串,因此这个操作三也合法,然后位置的差别可以通过在之后进行同样的操作来修正。
假如操作串与输入的
这个结论可以归纳地推广到任意一次操作三。
至此,我们可以先处理两种情况:不使用操作三,以及先通过某些操作使得
不使用操作三时,答案为
考虑单字符的情况:记
然后,枚举第一次操作三时的字符
第二种情况是
在后面,我们只考虑,进行操作三时的操作串是
结论 2
一定存在一种最优解的方案是,先进行若干次操作一和操作二,然后进行一次操作三,然后再进行若干次操作一。
简要说明一下:考虑假如你在两次操作三之间进行了一次其他操作,将这个操作移到所有操作三的前面也是合法的。
这样的话,所有操作三是连续的一段,显然可以只进行一次。
因此:这种情况是,先收缩成一段,然后进行一次操作三,然后归位。
我们考虑操作三之后
第一种情况是
因为,这样操作之后需要进行若干次操作使
第二种情况是
把答案写出来,然后去掉常数,发现我们要做的其实是,对于所有在
暴力1
固定
二分
算上枚举
暴力2
固定长度,则
首先考虑
对每个点维护子树中的所有在
有
注意到这样只能找到一个等价类内最长串的答案,易知这不劣。
对每个
不暴力了
相信各位经验丰富的选手看到这里时,正解已经呼之欲出了。
考虑用上述两个暴力来摊平查询的复杂度。考虑按
根据第一个暴力的复杂度,取适当的
std 写的是
下面是 std,需要可以自取
#include <bits/stdc++.h>
#define maxn 30005
const int B=40;
const int inf=1e8;
int n,q,ch[maxn<<1][2],anc[maxn<<1][17]={0},
depth[maxn<<1]={0},rank[maxn],tail=0,rk[maxn<<1],dp[maxn<<1],min[maxn<<1],tl,
ans[maxn],d[maxn<<1],vis[30],dis[27][maxn],pre[maxn<<1],fa[maxn<<1],max[maxn<<1],tag[maxn<<1],
cnt[26][maxn];
std::vector<int>rc[27];
char s[maxn];
struct Q{int i,j,p,id;}qr[maxn];
int cmp1(Q a,Q b){return a.p>b.p;}
int cmp(int a,int b){
if (depth[a]!=depth[b])return depth[a]<depth[b];
return d[a]<d[b];
}struct Q2{
int l,r,p,id,k;
}q1[maxn*B];
int cmp2(Q2 a,Q2 b){return a.l>b.l;}
void cover(int x,int y){tag[x]=std::min(tag[x],y);max[x]=std::min(max[x],y);}
void pushdown(int x){
if(ch[x][0])cover(ch[x][0],tag[x]);
if(ch[x][1])cover(ch[x][1],tag[x]);
tag[x]=inf;
}
int not_root(int x) {return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
void rotate(int x) {
int f=fa[x],g=fa[f],l=ch[f][1]==x,c=ch[x][l^1];
if (not_root(f)) ch[g][ch[g][1]==f]=x;
ch[x][l^1]=f;ch[f][l]=c;
if (c) fa[c]=f;
fa[f]=x;fa[x]=g;
}
void spaly(int x) {
std::stack<int>stk;
int u=x;stk.push(u);while(not_root(u)){stk.push(fa[u]);u=fa[u];}
while(!stk.empty()){pushdown(stk.top());stk.pop();}
while(not_root(x)){
rotate(x);
if (not_root(fa[x])&¬_root(x))
rotate((ch[fa[x]][1]==x)!=(ch[fa[fa[x]]][1]==fa[x])?fa[x]:x);
}
}
struct suffixTree {
int link[maxn<<1],len[maxn<<1],start[maxn<<1],s[maxn<<1],tail,n,rem,now,ch[maxn<<1][27];
suffixTree ():tail(1),n(0),rem(0),now(1) {len[0]=inf;}
int newnode(int st,int le){
link[++tail]=1;start[tail]=st;len[tail]=le;return tail;
}
void extend(int x){
s[++n]=x;rem++;
for (int last=1;rem;){
while (rem>len[ch[now][s[n-rem+1]]]) rem-=len[now=ch[now][s[n-rem+1]]];
int &v=ch[now][s[n-rem+1]];int c=s[start[v]+rem-1];
if (!v||x==c){
link[last]=now;last=now;
if (!v) v=newnode(n-rem+1,inf);
else break;
}else{
int u=newnode(start[v],rem-1);
ch[u][c]=v;ch[u][x]=newnode(n,inf);
start[v]+=rem-1;len[v]-=rem-1;
link[last]=v=u;last=u;
} if (now==1) rem--;else now=link[now];
}
}
}sft;
void dfs(int u,int f){
anc[u][0]=f;
for (int i=1;i<=15;++i)anc[u][i]=anc[anc[u][i-1]][i-1];
int len=std::min(sft.len[u],n-sft.start[u]+1),isleaf=1;
pre[u]=fa[u]=f;max[u]=tag[u]=inf;
depth[u]=depth[f]+len;d[u]=d[f]+1;
for (int i=0;i<=26;++i)
if (sft.ch[u][i])dfs(sft.ch[u][i],u),isleaf=0;
if (isleaf)
rank[n-depth[u]+1]=u;
rk[++tl]=u;
}
void access(int x,int w) {
for (int y=0;x;x=fa[y=x])spaly(x),ch[x][1]=y,cover(x,w);
}
int query(int x,int r){
if(!x)return 0;
pushdown(x);
if(max[x]<r-depth[pre[x]]+1){
int d=query(ch[x][1],r);
if(d)return d;else return std::min(depth[x],r-max[x]+1);
}return query(ch[x][0],r);
}
int find(int x,int r){
for(int y=0;x!=1&&x;x=fa[y=x]){
spaly(x);ch[x][1]=0;
int u=x;while(ch[u][0]){pushdown(u);u=ch[u][0];}
if(max[u]<r-depth[pre[u]]+1)
return query(x,r);
ch[x][1]=y;
}return 0;
}
void rebuild(int l){
std::memset(dp,63,sizeof(dp));
for(int i=l;i<=n;++i)min[rank[i]]=i;
for(int i=tl;i>=1;i--)min[anc[rk[i]][0]]=std::min(min[rk[i]],min[anc[rk[i]][0]]);
for(int j=2;j<=tl;++j){
int i=rk[j];
dp[i]=min[i]-depth[i]+1;
dp[i]=std::min(dp[i],std::min(dp[anc[i][0]],sft.link[i]!=1?dp[sft.link[i]]:inf));
}
}
int locate(int l,int r){
int u=rank[l];
for (int i=15;i>=0;i--)
if (depth[anc[u][i]]>=(r-l+1))u=anc[u][i];
return u;
}int lca(int u,int v){
if (d[u]<d[v])std::swap(u,v);
for(int i=15;i>=0;i--)if(d[anc[u][i]]>=d[v])u=anc[u][i];
if (u==v)return u;
for(int i=15;i>=0;i--)if(anc[u][i]!=anc[v][i])u=anc[u][i],v=anc[v][i];
return anc[u][0];
}
void bfs(int c){
std::queue<int>q;
std::memset(vis,0,sizeof(vis));
std::memset(dis[c],63,sizeof(dis[c]));
for(int v:rc[c]){q.push(v);dis[c][v]=0;}
vis[c]=1;
while(!q.empty()){
int x=q.front();int d=s[x]-'a';q.pop();
if(!vis[d]){
vis[d]=1;
for(int v:rc[d])
if(dis[c][v]>n){dis[c][v]=dis[c][x]+1;q.push(v);}
}if (x>1&&dis[c][x-1]>n){dis[c][x-1]=dis[c][x]+1;q.push(x-1);}
if(x<n&&dis[c][x+1]>n){dis[c][x+1]=dis[c][x]+1;q.push(x+1);}
}
}
void prework(){
std::memset(min,63,sizeof(min));
sft.link[1]=1;dfs(1,0);std::sort(rk+1,rk+tl+1,cmp);
for(int i=1;i<n;++i)sft.link[rank[i]]=rank[i+1];
for(int i=1;i<=n;++i)rc[s[i]-'a'].push_back(i);
for(int i=0;i<26;++i)bfs(i);
}
int corner_cases(int i,int j,int p){
int ans=abs(i-p);
for(int c=0;c<26;++c){
ans=std::min(ans,j-i+dis[c][i]+dis[c][p]+1);
ans=std::min(ans,j-i+dis[c][j]+dis[c][p]+1);
if(cnt[c][j]-cnt[c][i-1])ans=std::min(ans,j-i+dis[c][p]+1);
}return ans;
}
int main(){
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for (int i=1;i<=n;++i){
sft.extend(s[i]-'a');
for(int c=0;c<26;++c)cnt[c][i]=cnt[c][i-1];
cnt[s[i]-'a'][i]++;
}
sft.extend(26);prework();
for(int i=1;i<=q;++i){
scanf("%d%d%d",&qr[i].i,&qr[i].j,&qr[i].p);qr[i].id=i;
int len=depth[lca(rank[qr[i].i],rank[qr[i].j])];
qr[i].j=qr[i].i+len-1;
}std::sort(qr+1,qr+q+1,cmp1);
int cnt=0,last=n,p=0;
rebuild(n);
for(int i=n;i>=1;--i){
cnt++;
if (cnt>B){rebuild(i);last=i;cnt=0;}
while(p<q&&qr[p+1].p==i){
++p;
ans[qr[p].id]=
std::min(qr[p].j-qr[p].i+1+dp[locate(qr[p].i,qr[p].j)]-qr[p].p,
corner_cases(qr[p].i,qr[p].j,qr[p].p));
for(int j=i;j<last;++j){
q1[++tail].l=qr[p].i;q1[tail].r=qr[p].j;q1[tail].p=j;
q1[tail].id=qr[p].id;
q1[tail].k=j-i+qr[p].j-qr[p].i+2;
}
}
}std::sort(q1+1,q1+tail+1,cmp2);
p=0;
for(int i=n;i>=1;--i){
access(rank[i],i);
while(p<tail&&q1[p+1].l==i){
++p;int d=find(rank[q1[p].p],q1[p].r);
if(d)ans[q1[p].id]=std::min(ans[q1[p].id],q1[p].k-d);
}
}
for(int i=1;i<=q;++i)ans[qr[i].id]+=n-qr[i].j;
for(int i=1;i<=q;++i)printf("%d\n",ans[i]);
return 0;
}