[HNOI2019] JOJO

题意:

初始有一个空串,有n次操作:

  • $(1,x,c)$,表示在当前串后添加x个字符c,保证c不同于当前串末尾的字符。
  • $(2,x)$,表示将当前串变成第x次操作后的串。

每次操作完你需要输出$\sum \limits_{i=1}^{n}{nxt(i)}$,其中$nxt(i)$与kmp中的nxt同义。

$n\leq 10^{5},x\leq 10^{4}$。

 

题解:

首先考虑怎么将普通kmp扩展到字符块的kmp。先假设没有2操作。

将每个块视作一个字符(二元组$(x,c)$)建一个新串,容易发现如果某段前缀和后缀这两个串相匹配,则满足

  • 两个串的二元组个数相等。
  • 两个串除了首尾其他二元组均相等。
  • 后缀首位的x$\geq$前缀首位的x,前缀末位的x$\geq $后缀元素的x。

那么显然可以记$nxt'(i)$表示新串的nxt。两个$\geq$不好处理,我们令$nxt'(i)$匹配的末位元素必须对位相等。

此时就可以仿照kmp的做法跳指针,并在跳的同时更新当前二元组的贡献(大概是不断取max做等差数列求和),就解决了没有2操作的问题。

考虑2操作,对于不强制在线的题目,有一个套路是把操作离线下来建成一棵树,dfs即可。

但kmp的复杂度是均摊的,上树直接做怕是不行,一条链底下挂个菊花就能把这个做法卡飞。

考虑优化,容易发现复杂度错误的时候这个串一定是一个循环串,而且x都相等不影响更新答案,那么每次直接跳到第一个循环节即可计算。

注意遇到一个循环串应该先跳一下nxt判断能不能匹配而不是直接跳到头,这样不符合nxt极大的性质。

其实也可以主席树维护kmp自动机做。但是我过了就不想写了。

复杂度$O(n\log{n})$,跳的时候注意边界。

 

套路:

  • 遇到扩展问题先想一下能不能用原问题的思路做。
  • 不强制在线有撤回操作$\rightarrow$将操作离线建树dfs处理。

 

代码:

[HNOI2019] JOJO
#include<bits/stdc++.h>
#define maxn 200005
#define maxm 500005
#define inf 0x7fffffff
#define mod 998244353
#define ll long long
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
ll siz[maxn],id[maxn],fa[maxn],res[maxn];
ll sum[maxn],nxt[maxn],ind[maxn];
vector<ll> to[maxn]; char ch[maxn];

inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline void dfs(ll u,ll n){
    if(n==0){for(rint i=0;i<to[u].size();i++){ll v=to[u][i];dfs(v,n+1);}return;} 
    id[n]=u,sum[n]=sum[n-1]+siz[u],res[u]=res[fa[u]];
    if(n==1){
        nxt[n]=0; ll a1=0,an=siz[u]-1;
        res[u]+=(a1+an)*(an-a1+1)/2,res[u]%=mod;
    } 
    else{
        ll j=nxt[n-1],hv=0,len=n-1-j;
        while(j && ((ch[id[j+1]]!=ch[u])||(siz[id[j+1]]!=siz[u]))){
            if(ch[id[j+1]]==ch[u] && siz[id[j+1]]>hv){
                ll a1=sum[j]+hv+1,an=sum[j]+min(siz[id[j+1]],siz[u]);
                res[u]+=(a1+an)*(an-a1+1)/2,res[u]%=mod,hv=min(siz[id[j+1]],siz[u]);
            }
            if(j-nxt[j]==len) j=j-nxt[j]; len=j-nxt[j],j=nxt[j];
        }
        if(ch[id[j+1]]!=ch[u]) nxt[n]=0;
        else{
            if(siz[id[j+1]]<=siz[u]){
                nxt[n]=j+1; ll a1=sum[j]+hv+1,an=sum[j+1];
                res[u]+=(a1+an)*(an-a1+1)/2,res[u]%=mod;
                res[u]+=(siz[u]-siz[id[j+1]])*sum[j+1],res[u]%=mod;
            }
            else{
                nxt[n]=0; ll a1=hv+1,an=siz[u];
                res[u]+=(a1+an)*(an-a1+1)/2,res[u]%=mod;
            } 
        }
    }
    for(rint i=0;i<to[u].size();i++){ll v=to[u][i];dfs(v,n+1);} 
}

int main(){
    //freopen("jojo7.in","r",stdin);
    //freopen("1.out","w",stdout);
    ll n=read();
    for(rint i=1;i<=n;i++){
        ll op=read();
        if(op==1) fa[i]=ind[i-1],ind[i]=i,siz[i]=read(),scanf("%c",&ch[i]);
        else{ll x=read();ind[i]=ind[x];}
    }
    for(rint i=1;i<=n;i++) if(ind[i]==i) to[fa[i]].push_back(i);
    for(rint i=0;i<=n;i++) if(to[i].size()) sort(to[i].begin(),to[i].end());
    dfs(0,0);
    for(rint i=1;i<=n;i++)
        printf("%lld\n",res[ind[i]]);
    return 0;
}
JOJO

 

上一篇:day50-时中


下一篇:树形背包复杂度+P3177 [HAOI2015]树上染色