洛谷P5115 Check,Check,Check one two! 边分治+虚树+SAM

绝对是我写过最长的一份代码了.   

这个快敲吐了. 

通过这道题能 get 到一个套路:

两颗树同时统计信息的题可以考虑在个树上跑边分治,把点扔到另一颗树的虚树上,然后跑虚树DP.

具体地,这道题中我们发现 $LCP$ 长度是反串后缀树 $LCA$ 深度,$LCS$ 是正串后缀树 $LCA$ 深度.

我们建出正反两串后缀树后,将长度大于 K1/K2 的点的深度置为 0,然后跑一个边分+虚树即可.

code: 

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>      
#define N 200007
#define inf 0x3f3f3f3f 
#define ull unsigned long long
  
// 代码已写完,人已阵亡.
      
using namespace std;
  
int bug;  
int K1,K2;
ull ans,W;
char S[N];   
  
namespace IO { 
  
    void setIO(string s)
    {
        string in=s+".in";
        string out=s+".out";
        freopen(in.c_str(),"r",stdin);
        // freopen(out.c_str(),"w",stdout);
    }
  
};
  
struct SAM {      
  
    #define M N<<1 
  
    int tot,last;   
    struct Edge { 
        int to,w;
        Edge(int to=0,int w=0):to(to),w(w){}  
    }; 
    vector<Edge>G[M];
    int pre[M],ch[M][26],mx[M],str_sam[M],sam_str[M],depth[M];             
  
    void Initialize() { tot=last=1; }   
  
    void extend(int c)
    {       
        int np=++tot,p=last; 
        mx[np]=mx[p]+1,last=np;
        for(;p&&!ch[p][c];p=pre[p]) ch[p][c]=np; 
        if(!p) pre[np]=1;
        else
        {
            int q=ch[p][c];
            if(mx[q]==mx[p]+1) pre[np]=q;
            else
            {
                int nq=++tot;   
                mx[nq]=mx[p]+1;  
                memcpy(ch[nq],ch[q],sizeof(ch[q]));  
                pre[nq]=pre[q],pre[np]=pre[q]=nq;  
                for(;p&&ch[p][c]==q;p=pre[p])  ch[p][c]=nq;
            }
        }    
    }
      
  
    void Build_LCP()
    {     
        int n=strlen(S+1),i,j,p=1;
        for(i=1;i<=n;++i)
        {
            p=ch[p][S[n-i+1]-'a']; 
            sam_str[p]=n-i+1;
            str_sam[n-i+1]=p;                   
        }
        for(i=2;i<=tot;++i)
        {
            if(mx[i]>K1) depth[i]=0;         
            else depth[i]=mx[i]; 
        }
        for(i=2;i<=tot;++i)  G[pre[i]].push_back(Edge(i,depth[i]-depth[pre[i]]));       
    } 
      
    void Build_LCS()
    {
        int n=strlen(S+1),i,j,p=1; 
        for(i=1;i<=n;++i)
        {
            p=ch[p][S[i]-'a']; 
            sam_str[p]=i;
            str_sam[i]=p; 
        }  
        for(i=2;i<=tot;++i)
        {
            if(mx[i]>K2) depth[i]=0;
            else  depth[i]=mx[i];
        }
        for(i=2;i<=tot;++i)  G[pre[i]].push_back(Edge(i,depth[i]-depth[pre[i]]));   
    }   
  
    #undef M
  
}lcp,lcs;
  
namespace vir {    
  
    vector<int>G[N<<2];
    vector<int>clr;  

    int t,sta,tot;
    int is1[N<<2],is2[N<<2];
    int dfn[N<<2],dep[N<<2],size[N<<2],son[N<<2],top[N<<2],f[N<<2];     
    int S[N<<2],val[N<<2],re[N<<2];
    ull size1[N<<2],size2[N<<2];   
    ull sum1[N<<2],sum2[N<<2];        
  
    bool cmp(int a,int b)
    {
        return dfn[a]<dfn[b]; 
    }
  
    void get_dfn(int x,int fa)
    {       
        dfn[x]=++t;         
        size[x]=1;
        f[x]=fa; 
        for(int i=0;i<lcp.G[x].size();++i)
        {
            int y=lcp.G[x][i].to; 
            if(y==fa) continue;
            dep[y]=dep[x]+1;  
            get_dfn(y,x);     
            size[x]+=size[y];
            if(size[y]>size[son[x]]) son[x]=y;
        }      
    } 
  
    void dfs2(int u,int tp)
    {
        top[u]=tp;
        if(son[u]) dfs2(son[u],tp);
        for(int i=0;i<lcp.G[u].size();++i)
        {
            int v=lcp.G[u][i].to;  
            if(v==son[u]||v==f[u]) continue;   
            dfs2(v,v);  
        }
    }
  
    int LCA(int x,int y)
    {
        while(top[x]!=top[y])
        {
            dep[top[x]]>dep[top[y]]?x=f[top[x]]:y=f[top[y]];
        }
        return dep[x]<dep[y]?x:y;
    }
  
    void _new(int x,int v,int c)
    {
        ++tot;
        re[tot]=x;
        val[x]=v;
        if(c==1) is1[x]=1;
        else is2[x]=1;
    }  
  
    void addvir(int x,int y)
    { 
        G[x].push_back(y);
    }
  
    void Initialize()
    {
        t=0; 
        get_dfn(1,0); 
        dfs2(1,1);          
    }     
  
    void Insert(int x)
    {
        if(sta<=1)
        {
            S[++sta]=x;    
            return;
        }
        int lca=LCA(S[sta],x);     
        if(lca==S[sta]) S[++sta]=x;
        else
        {
            while(sta>1&&dep[S[sta-1]]>=dep[lca]) addvir(S[sta-1],S[sta]),--sta;  
            if(S[sta]==lca)  S[++sta]=x;                         
            else
            {
                addvir(lca,S[sta]); 
                S[sta]=lca;
                S[++sta]=x; 
            } 
        }
    }
  
    void Build()
    {  
        sta=0;
        sort(re+1,re+1+tot,cmp);                         
        if(re[1]!=1) S[++sta]=1;         
        for(int i=1;i<=tot;++i) Insert(re[i]);
        while(sta>1)  addvir(S[sta-1],S[sta]),--sta;                       
    }  
  
    void DP(int x)
    {   
        clr.push_back(x); 
        for(int i=0;i<G[x].size();++i)
        {
            int y=G[x][i];
            DP(y);          
            size1[x]+=size1[y];
            size2[x]+=size2[y]; 
            sum1[x]+=sum1[y]; 
            sum2[x]+=sum2[y];             
        }
        ull tmp=0; 
        ull cntw=0;
        ull cur=0; 
        for(int i=0;i<G[x].size();++i)
        { 
            int y=G[x][i];   
            tmp+=(sum1[x]-sum1[y])*size2[y];      
            tmp+=(sum2[x]-sum2[y])*size1[y];            
            cntw+=(size1[x]-size1[y])*size2[y];
        }  
        cur+=tmp*lcp.depth[x];         
        cur-=cntw*W*lcp.depth[x]; 
        if(is1[x])
        {  
            cur+=size2[x]*val[x]*lcp.depth[x];  
            cur+=sum2[x]*lcp.depth[x];  
            cur-=size2[x]*W*lcp.depth[x]; 
        } 
        if(is2[x])
        {
            cur+=size1[x]*val[x]*lcp.depth[x]; 
            cur+=sum1[x]*lcp.depth[x];  
            cur-=size1[x]*W*lcp.depth[x]; 
        }     
        ans+=cur/2;  
        size1[x]+=is1[x];
        size2[x]+=is2[x]; 
        sum1[x]+=is1[x]*val[x];
        sum2[x]+=is2[x]*val[x]; 
        G[x].clear();       
    }
  
    void solve()
    {
        Build(); 
        DP(1); 
        for(int i=0;i<clr.size();++i) 
        {
            int x=clr[i];   
            val[x]=sum1[x]=sum2[x]=size1[x]=size2[x]=is1[x]=is2[x]=0;    
        }           
        for(int i=1;i<=tot;++i)
        {         
            re[i]=0; 
        }
        tot=0; 
        sta=0;      
        clr.clear(); 
    }
};  // 虚树
  
int tot,edges=1;
int totsize,rt1,rt2,mx,ed,lsc,rsc;
int hd[N<<2],vis[N<<3],size[N<<2];
  
struct Edge {
    int to,w,nex; 
}e[N<<3];  
   
struct Node { 
    int u,dis,val;      
    Node(int u=0,int dis=0,int val=0):u(u),dis(dis),val(val){}         
}L[N<<2],R[N<<2];
  
void add_div(int x,int y,int z)
{
    e[++edges].nex=hd[x],hd[x]=edges,e[edges].to=y,e[edges].w=z; 
}  
  
void Build_Tree(int x,int fa)
{
    int ff=0;             
    for(int i=0;i<lcs.G[x].size();++i)
    {
        int y=lcs.G[x][i].to; 
        if(y==fa)  continue;  
        if(!ff)
        {
            ff=x;  
            add_div(ff,y,lcs.G[x][i].w);
            add_div(y,ff,lcs.G[x][i].w);  
        }
        else
        {
            ++tot;        
            add_div(ff,tot,0);
            add_div(tot,ff,0);                         
            add_div(tot,y,lcs.G[x][i].w); 
            add_div(y,tot,lcs.G[x][i].w); 
            ff=tot;        
        }
        Build_Tree(y,x); 
    }
}
  
void find_edge(int x,int fa)
{  
    size[x]=1;
    for(int i=hd[x];i;i=e[i].nex)
    {
        int y=e[i].to; 
        if(y==fa||vis[i])  continue;  
        find_edge(y,x);
        int now=max(size[y],totsize-size[y]);  
        if(now<mx)
        {
            mx=now;
            ed=i; 
            rt1=y; 
            rt2=x;  
        }
        size[x]+=size[y];
    }
}
  
  
void get_node(int x,int fa,int dep,int ty)
{     
    if(ty==1)
    {           
        if(lcs.sam_str[x])  L[++lsc]=Node(lcs.sam_str[x],lcs.depth[x]-dep,dep); 
    }
    else
    {  
        if(lcs.sam_str[x])  R[++rsc]=Node(lcs.sam_str[x],lcs.depth[x]-dep,dep);
    }
    for(int i=hd[x];i;i=e[i].nex)
    {
        int y=e[i].to;   
        if(vis[i]||y==fa)  continue;  
        get_node(y,x,dep+e[i].w,ty); 
    }
} 

void Divide_And_conquer(int x)
{      
    if(totsize==1) return; 
    mx=inf;  
    rt1=rt2=ed=0;     
    find_edge(x,0);             
    vis[ed]=vis[ed^1]=1;    
    lsc=rsc=0;                                      
    get_node(rt1,0,0,1); 
    get_node(rt2,0,0,2);            
    W=(ull)e[ed].w;  
    ull tmp=ans;   
  
    for(int i=1;i<=lsc;++i) vir::_new(lcp.str_sam[L[i].u],L[i].dis,1); 
    for(int i=1;i<=rsc;++i) vir::_new(lcp.str_sam[R[i].u],R[i].dis,2); 
    vir::solve();                    

    int tmprt1=rt1,tmprt2=rt2;           
    int sizert1=size[rt1],sizert2=totsize-size[rt1];           
    totsize=sizert1;      
    Divide_And_conquer(tmprt1);    
    totsize=sizert2; 
    Divide_And_conquer(tmprt2); 
}
  
int main()
{
    // IO::setIO("input");
    int i,j,n; 
    scanf("%s%d%d",S+1,&K1,&K2);        
    n=strlen(S+1);    
    lcp.Initialize(); 
    lcs.Initialize(); 
    for(i=1;i<=n;++i)
    {    
        lcs.extend(S[i]-'a'); 
        lcp.extend(S[n-i+1]-'a'); 
    }
    lcs.Build_LCS(); 
    lcp.Build_LCP();
    tot=lcs.tot;     
    Build_Tree(1,0); 
    vir::Initialize();    
    totsize=tot;
    Divide_And_conquer(1);
    printf("%llu\n",ans); 
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值