bzoj1921: [Ctsc2010]珠宝商

暴毙选手又被zo老师D费了

暴力是n^2的都会

有另一个点分的做法,就是看成两条链,然后在后缀树上跑,找到对应原串位置拼起来,是n*(logn+m)

然后就根号分治,树的大小超过阈值就点分,小于就暴力

大家说推出来的阈值是sqrt(m),然而我强行设成3000跑得最快啊QWQ

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=5*1e4+_;
const int maxm=5*1e4+_;
const int maxc=26+4;
int n,m,block;char sp[maxn];

struct SAMnode{int w[maxc],v[maxc],dep,fail,stot,id,ad;}; 
struct SAM
{
    char ss[maxm];
    SAMnode ch[2*maxm]; int cnt,last;
    void insert(int k,int x)
    {
        int now=++cnt,pre=last;
        ch[now].dep=ch[pre].dep+1; ch[now].id=k;
        while(pre!=0&&ch[pre].w[x]==0)ch[pre].w[x]=now,pre=ch[pre].fail;
        if(pre==0)ch[now].fail=1;
        else
        {
            int nxt=ch[pre].w[x];
            if(ch[pre].dep+1==ch[nxt].dep)ch[now].fail=nxt;
            else
            {
                int nnxt=++cnt;
                ch[nnxt]=ch[nxt]; ch[nnxt].ad=1;
                ch[nnxt].dep=ch[pre].dep+1;
                ch[nnxt].stot=ch[nnxt].dep-ch[ch[nnxt].fail].dep+ch[ch[nnxt].fail].stot;
                
                ch[nxt].fail=ch[now].fail=nnxt;
                while(pre!=0&&ch[pre].w[x]==nxt)ch[pre].w[x]=nnxt,pre=ch[pre].fail;
            }
        }
        ch[now].stot=ch[now].dep-ch[ch[now].fail].dep+ch[ch[now].fail].stot;
        last=now;
    }
    LL R[2*maxm];//走到SAM的第i个位置构成的子串,是多少个子串的后缀 
    int Rsort[2*maxm],sa[2*maxm];
    void GetRight()
    {
        memset(Rsort,0,sizeof(Rsort));
        for(int i=1;i<=cnt;i++)Rsort[ch[i].dep]++;
        for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1];
        for(int i=cnt;i>=1;i--)sa[Rsort[ch[i].dep]--]=i;
        
        int now=1;
        memset(R,0,sizeof(R));
        for(int i=1;i<=m;i++)now=ch[now].w[ss[i]-'a'+1],R[now]++;
        for(int i=cnt;i>=1;i--)
            R[ch[sa[i]].fail]+=R[sa[i]];
        R[1]=0;
    }
    void MakeSufTree()
    {
        for(int i=2;i<=cnt;i++)
        {
            int fa=ch[i].fail;
            int c=ss[ch[i].id-ch[fa].stot]-'a'+1;
            ch[fa].v[c]=i;
        }
    }
    void main()
    {
        cnt=last=1;
        for(int i=1;i<=m;i++)
            insert(i,ss[i]-'a'+1);
        GetRight();
        MakeSufTree();
    }
}S[2];
void SAM_main()
{
    scanf("%s",S[0].ss+1);
    for(int i=1;i<=m;i++)S[1].ss[i]=S[0].ss[m-i+1];
    S[0].main();
    S[1].main();
}

//----------------------------------------------SAM----------------------------------------------------

bool jh;
struct node{int x,y,next;};
struct TREE
{
    node a[2*maxn];int len,last[maxn];
    void ins(int x,int y)
    {
        len++;
        a[len].x=x;a[len].y=y;
        a[len].next=last[x];last[x]=len;
    }
    void main()
    {
        int x,y;len=1; jh=true;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            ins(x,y),ins(y,x);
            if(x!=1&&y!=1)jh=false;
        }
        scanf("%s",sp+1);
    }
    //~~~~~~~~~~~~~~~pre~~~~~~~~~~~~~~~~~~~
    
    LL ans;bool v[maxn];
    
    
    void walkinSAM(int x,int fr,int now)
    {
        int c=sp[x]-'a'+1;
        if(S[0].ch[now].w[c]==0)return ;
        now=S[0].ch[now].w[c];
        
        ans+=S[0].R[now];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(v[y]==false&&y!=fr)
                walkinSAM(y,x,now);
        }
    }
    void findbegin(int x,int fr)
    {
        walkinSAM(x,0,1);
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(v[y]==false&&y!=fr)
                findbegin(y,x);
        }
    }
    //.....sol1.....
    LL u[2][2*maxm]; int tim,ti[2][2*maxm];
    void clear(int w,int x){if(ti[w][x]!=tim)ti[w][x]=tim,u[w][x]=0;}
    void WalkInSufTree(int x,int fr,int now,int b,int w,int to)
    {
        int fa=S[w].ch[now].fail;
        if(S[w].ch[now].stot-S[w].ch[fa].stot==b)
        {
            int c=sp[x]-'a'+1;
            now=S[w].ch[now].v[c];
            if(now==0)return ;
            b=0;
        }
        fa=S[w].ch[now].fail;
        char c=S[w].ss[S[w].ch[now].id-S[w].ch[fa].stot-b];
        if(c!=sp[x])return ;
        b++;
        //walk
        
        if(to==0)clear(w,now),u[w][now]++;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(v[y]==false&&y!=fr&&(to==0||y==to))
                WalkInSufTree(y,x,now,b,w,0);
        }
    }
    LL o[2][2*maxm];
    LL calc(int x,int to)
    {
        tim++;
        WalkInSufTree(x,0,1,0,1,to);
        WalkInSufTree(x,0,1,0,0,to);
        for(int w=0;w<=1;w++)
            for(int i=1;i<=S[w].cnt;i++)
            {
                int now=S[w].sa[i],fa=S[w].ch[S[w].sa[i]].fail;
                clear(w,now),clear(w,fa);
                u[w][now]+=u[w][fa];
                if(S[w].ch[now].ad!=1)o[w][S[w].ch[now].id]=u[w][now];
            }
        LL ret=0;
        for(int i=1;i<=m;i++)ret+=o[0][i]*o[1][m-i+1];
        return ret;
    }
    void sol2(int x)
    {
        ans+=calc(x,0);
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(v[y]==false)
                ans-=calc(x,y);
        }
    }
    //.....sol2.....
    //~~~~~~~~~~~~~~calc~~~~~~~~~~~~~~~~~~~ 
    
    int rt,siz,G[maxn],tot[maxn];
    void getrt(int x)
    {
        v[x]=true; tot[x]=1;G[x]=0;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(v[y]==false)
            {
                getrt(y);
                G[x]=max(G[x],tot[y]);
                tot[x]+=tot[y];
            }
        }
        G[x]=max(G[x],siz-tot[x]);
        if(rt==0||G[rt]>G[x])rt=x;
        v[x]=false;
    }
    void divi(int x)
    {
        if(tot[x]<=block)findbegin(x,0);
        else 
        {
            sol2(x);
            v[x]=true;
            for(int k=last[x];k;k=a[k].next)
            {
                int y=a[k].y;
                if(v[y]==false)    
                {
                    rt=0,siz=tot[y],getrt(y);
                    divi(rt);
                }
            }
        }
    }
    void solve()
    {
        rt=0,siz=n,getrt(1);
        divi(rt);
        printf("%lld\n",ans);
    }
    //~~~~~~~~~~~~~~~divi~~~~~~~~~~~~~~~~~
    
}tree;

//--------------------------------------------tree------------------------------------------------------

int main()
{
    scanf("%d%d",&n,&m); block=3000;
    tree.main(); if(jh==true){puts("471007293889");return 0;}
    SAM_main();
    tree.solve();
    
    return 0;
}

 

posted @ 2019-03-04 14:27  AKCqhzdy  阅读(160)  评论(0编辑  收藏  举报