• [CTSC2010]珠宝商 SAM+后缀树+点分治


    [CTSC2010]珠宝商 

    不错的题目

    看似无法做,n<=5e4,8s,根号算法?

    暴力一:

    n^2,+SAM上找匹配点的right集合sz,失配了直接退出

    暴力二:

    O(m)

    统计过lca=x的路径,

    没法直接合并,就间接合并!

    把所有形如(z,x)(x,y)的路径在原串所有出现位置打上标记

    原串每个点维护结束路径(zi,x)和开始路径(x,yi)个数(实际上只有char=a[x]的位置才有值),然后乘起来就是贡献

    打标记:

    SAM匹配

    麻烦事是:这个是往某个字符串前面加字符,但是要在字符串末尾位置打上标记

    (和通常SAM末尾+字符,末尾位置打标记不同)

    要用到后缀树、前缀树

    (z,x)路径是前缀树

    (x,y)路径是后缀树(反串前缀树)

    把parent树建成前缀树,走一下。

    建树:

    (第一次建后缀树)

    1.每个节点随便记录一个parent树子树里的后缀出现位置pos[x]

    2.预处理parent树每个边第一个字符(最多26条边),

    3.可能在边上,所以状态用(p,l)记录:下面是p点,在p点上方l处。

    trans,根据l大小进行讨论

    打标记:

    trans打上tag

    最后直接下放

    结合

    设阈值B

    size<=B用暴力一,不递归了,O(B^2*n/B)

    size>B用暴力二,分治树节点个数不超O(B)最多进行O(Bm)

    B取sqrt(n)即可

    容斥?

    暴力二离线处理共线,显然会重复。分治到下一层的时候为上一层去重。

    也是根据size选择去重方法

    Code

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define fi first
    #define se second
    #define mk(a,b) make_pair(a,b)
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    template<class T>il void rd(T &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
    template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
    template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('
    ');}
    
    namespace Miracle{
    const int N=50005;
    int B;
    char s[N];
    int n,m;
    char a[N];
    struct node{
        int nxt,to;
    }e[2*N];
    int hd[N],cnt;
    void add(int x,int y){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        hd[x]=cnt;
    }
    ll ans;
    struct SAM{
        int ch[2*N][26],cnt,len[2*N],fa[2*N];
        int son[2*N][26],nd;
        int tag[2*N],exi[2*N],ad[2*N],pos[2*N];
        int sz[2*N];
        char s[2*N];
        SAM(){
            cnt=nd=1;
        }
        void ins(int c,int l){
            int p=nd;len[nd=++cnt]=l;
            exi[cnt]=l;sz[cnt]=1;
            pos[cnt]=l;
            
            for(;p&&ch[p][c]==0;p=fa[p]) ch[p][c]=nd;
            if(!p){
                fa[nd]=1;return;
            }
            int q=ch[p][c];
            if(len[q]==len[p]+1){
                fa[nd]=q;return;
            }
            len[++cnt]=len[p]+1;
            fa[cnt]=fa[q];fa[q]=fa[nd]=cnt;
            for(reg i=0;i<26;++i) ch[cnt][i]=ch[q][i];
            for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=cnt;
        }
        struct edge{
            int nxt,to;
        }e[2*N];
        int hd[2*N],tot;
        void add(int x,int y){
            e[++tot].nxt=hd[x];
            e[tot].to=y;hd[x]=tot;
        }
        void dfs(int x){//pushup sz!!!
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                dfs(y);
                sz[x]+=sz[y];
                if(!pos[x]) pos[x]=pos[y];
            }
        }
        void pre(){
    //        cout<<s+1<<endl;
            for(reg i=1;i<=m;++i) ins(s[i]-'a',i);
            for(reg i=2;i<=cnt;++i) add(fa[i],i);
            dfs(1);
        }
        void build(){
            for(reg i=2;i<=cnt;++i){
                son[fa[i]][s[pos[i]-len[fa[i]]]-'a']=i;
            }
        }
    //    int jump(int p,int c){//return to cur ; 
    //        return ch[p][c];
    //    }
        void trans(pair<int,int>&st,int c,int ok){//and add tag
            if(st.fi==0) return;
            if(st.se==1){//to son
                st.fi=son[st.fi][c];
                st.se=len[st.fi]-len[fa[st.fi]];
            }else{
                int to=pos[st.fi]-len[st.fi]+st.se-1;
                if(s[to]-'a'==c){
                    st.se--;
                }else st.fi=0;
            }
            if(st.fi!=0&&ok){
                ++tag[st.fi];
            }
            return;
        }
        void pushdown(int x){
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                tag[y]+=tag[x];
                pushdown(y);
            }
        }
        void calc(){
            pushdown(1);
    //        cout<<" cnt "<<cnt<<endl;
    //        prt(tag,1,cnt);
            for(reg i=1;i<=cnt;++i){
                if(exi[i]) ad[exi[i]]+=tag[i];
            }
    //        prt(ad,1,m);
        }
        void clear(){
            memset(tag,0,sizeof tag);
            memset(ad,0,sizeof ad);
        }
    }sam1,sam2;//sam1:pre tree ///// sam2:suf tree
    bool vis[N];
    int sz[N],nowsz;
    int rt;
    void dfs3(int x,int fa,int p,int w){//sz < B // w=1/-1
        p=sam1.ch[p][a[x]-'a'];
        if(p){
            ans+=(ll)w*sam1.sz[p];
        }
        else return ;//warinnig!!
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa||vis[y]) continue;
            dfs3(y,x,p,w);
        }
    }
    int sta[N],top;
    void dfs2(int x,int fa){//qu chong
        sta[++top]=x;
        int p=1;
        for(reg i=top;i>=1;--i) p=sam1.ch[p][a[sta[i]]-'a'];
        dfs3(sta[2],sta[1],p,-1);
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(vis[y]||y==fa) continue;
            dfs2(y,x);
        }
        sta[top--]=0;
    }
    void dfs4(int x,int fa){
        dfs3(x,0,1,1);
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa||vis[y]) continue;
            dfs4(y,x);
        }
    }
    
    void fin(int x,int fa){
        sz[x]=1;
        int mxsz=0;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;if(vis[y]||y==fa) continue;
            fin(y,x);
            sz[x]+=sz[y];
            mxsz=max(mxsz,sz[y]);
        }
        mxsz=max(mxsz,nowsz-sz[x]);
        if(mxsz<=nowsz/2){
            rt=x;
        }
    }
    void dfs1(int x,int fa,pair<int,int>p1,pair<int,int>p2){
        sz[x]=1;
        sam1.trans(p1,a[x]-'a',1);
        sam2.trans(p2,a[x]-'a',1);
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(vis[y]||y==fa) continue;
            dfs1(y,x,p1,p2);
            sz[x]+=sz[y];
        }
    }
    
    void divi(int x,int fa){
    //    cout<<" divi "<<x<<" fa "<<fa<<" ans "<<ans<<endl;
        if(nowsz<=B){
            if(fa){
                top=0;sta[++top]=fa;
                dfs2(x,0);
            }
            dfs4(x,0);
        }else{
            if(fa){
                sam1.clear();sam2.clear();
                pair<int,int>p1=mk(1,1),p2=mk(1,1);
                sam1.trans(p1,a[fa]-'a',0);
                sam2.trans(p2,a[fa]-'a',0);
                dfs1(x,0,p1,p2);
                //cout<<" a[fa] "<<a[fa]<<endl;
                sam1.calc();
                sam2.calc();
                
                for(reg i=1;i<=m;++i){
                    ans-=(ll)sam1.ad[i]*sam2.ad[m-i+1];
                    //cout<<" ii "<<i<<" "<<sam1.ad[i]<<" and "<<sam2.ad[m-i+1]<<endl;
                }
            }
    //        cout<<" after ans "<<ans<<endl;
    //        if(fa){
    //            top=0;sta[++top]=fa;
    //            dfs2(x,0);
    //        }
            
            rt=0;fin(x,0);    
            sam1.clear();sam2.clear();
            dfs1(rt,0,mk(1,1),mk(1,1));//warning!! start from sam's rt
            sam1.calc();
            sam2.calc();
            for(reg i=1;i<=m;++i){
                //if(s[i]==a[rt]) 
                ans+=(ll)sam1.ad[i]*sam2.ad[m-i+1];
            }
            
            vis[rt]=1;
            int tmp=rt;
            for(reg i=hd[rt];i;i=e[i].nxt){
                int y=e[i].to;
                if(!vis[y]) {
                    nowsz=sz[y];
                    divi(y,tmp);
                }
            }
        }
    }
    int main(){
        rd(n);rd(m);
        B=sqrt(n);
        int x,y;
        for(reg i=1;i<n;++i){
            rd(x);rd(y);add(x,y);add(y,x);
        }
        scanf("%s",s+1);
        for(reg i=1;i<=n;++i){
            a[i]=s[i];
        }
        scanf("%s",sam1.s+1);
        memcpy(sam2.s,sam1.s,sizeof sam1.s);
        reverse(sam2.s+1,sam2.s+m+1);
        sam1.pre();sam1.build();
        sam2.pre();sam2.build();
        
        nowsz=n;
        divi(1,0);
        cout<<ans;
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/4/9 17:06:25
    */
    View Code

    总结

    1.根号讨论暴力结合

    2.点分治合并是难点,

    要不然数据结构维护决策位置(树形Dp思路)

    要不然都把上下路径都求出来放在一起,再人工拼凑(容斥思路)

    3.有了前缀后缀树,妈妈再也不用担心一个串往前插入字符怎么匹配了!

  • 相关阅读:
    Nginx配置中一个不起眼字符"/"的巨大作用
    Logstash:多个配置文件(conf)
    2019年末Powershell 挖矿病毒的处理与防范
    2021年复习内容包括
    WPF 中双向绑定通知机制之ObservableCollection
    VB里Property用于标识一个类成员为属性而不是方法。
    KubeOperator
    页面布局-Grid,StackPanel,DockPanel,WrapPanel
    HTML-框架目录
    分析一段XAML语言代码
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10681077.html
Copyright © 2020-2023  润新知