【bzoj5084】 hashit(广义SAM+set)

题面

传送门

题解

后缀平衡树是个啥啊我不会啊……

那么我们来考虑一下\(SAM\)的做法好了

不难发现它的本义是要我们维护一棵\(trie\)树,并求出\(trie\)树上每一个节点到根的这段串的不同子串个数,而显然一个串的不同子串个数就是它的\(SAM\)上每一个节点的\(len[p]-len[fa[p]]\)之和

那么我们对这个\(trie\)建一个广义\(SAM\),这个\(SAM\)一定包含每一个路径的\(SAM\)

我们对每一个这棵\(trie\)上的每一个节点记录一个\(pos\),表示这个节点插入在\(SAM\)上的哪一个位置,然后把\(SAM\)\(parent\)树记录一下\(dfs\)

那么操作可以看做是在\(parent\)树上插入和删除节点,以插入为例,我们可以直接加上\(p\)到根节点的\(len\),但是这样有可能会算多,于是我们要减去\(p\)的前驱(以\(dfs\)序为顺序排列)和\(p\)\(LCA\)\(len\),以及和它的后继的\(LCA\)\(len\)。但是这样可能又减多了,所以还需要加上它的前驱和后继的\(LCA\)\(len\)

然后没有然后了

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define IT set<int>::iterator
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(char *s){
    R int len=0;R char ch;while(((ch=getc())>'z'||ch<'a')&&ch!='-');
    for(s[++len]=ch;(ch=getc())>='a'&&ch<='z'||ch=='-';s[++len]=ch);
    return s[len+1]='\0',len;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=2e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
char t[N];int l[N],ch[N][26],fa[N],pos[N],st[N],dep[N],dfn[N],top[N],sz[N],son[N],rk[N];
int las=1,cnt=1,n,tim;set<int> s;ll res;
void ins(int c,int p=las){
    if(ch[p][c]){
        int q=ch[p][c];
        if(l[q]==l[p]+1)las=q;
        else{
            int nq=las=++cnt;l[nq]=l[p]+1;
            memcpy(ch[nq],ch[q],4*26);
            fa[nq]=fa[q],fa[q]=nq;
            for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
        }
    }else{
        int np=las=++cnt;l[np]=l[p]+1;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(!p)fa[np]=1;
        else{
            int q=ch[p][c];
            if(l[q]==l[p]+1)fa[np]=q;
            else{
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],4*26);
                fa[nq]=fa[q],fa[q]=fa[np]=nq;
                for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
    }
}
void dfs1(int u){
    sz[u]=1,dep[u]=dep[fa[u]]+1;
    go(u){
        dfs1(v),sz[u]+=sz[v];
        sz[v]>sz[son[u]]?son[u]=v:0;
    }
}
void dfs2(int u,int t){
    top[u]=t,rk[dfn[u]=++tim]=u;
    if(!son[u])return;
    dfs2(son[u],t);
    go(u)if(!top[v])dfs2(v,v);
}
int LCA(int u,int v){
    while(top[u]!=top[v]){
        dep[top[u]]<dep[top[v]]?(swap(u,v),0):0;
        u=fa[top[u]];
    }return dep[u]<dep[v]?u:v;
}
void update(int u,int op){
    if(op==1)s.insert(u);
    IT itl,itr,it;itl=itr=it=s.find(u);
    res+=op*l[rk[u]];
    if(itl!=s.begin())--itl,res-=op*l[LCA(rk[u],rk[*itl])];
    if(itr!=--s.end())++itr,res-=op*l[LCA(rk[u],rk[*itr])];
    if(itl!=it&&itr!=it)res+=op*l[LCA(rk[*itl],rk[*itr])];
    if(op==-1)s.erase(u);
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read(t);
    st[0]=1;
    for(R int i=1,top=0;i<=n;++i)if(t[i]=='-')--top;
    else las=st[top],ins(t[i]-'a'),pos[i]=st[++top]=las;
    fp(i,2,cnt)add(fa[i],i);
    dfs1(1),dfs2(1,1);
    for(R int i=1,top=0;i<=n;++i){
        if(t[i]=='-')update(dfn[pos[st[top--]]],-1);
        else update(dfn[pos[st[++top]=i]],1);
        print(res);
    }
    return Ot(),0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10582931.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值