zoukankan      html  css  js  c++  java
  • BZOJ 4650: [Noi2016]优秀的拆分 后缀自动机+启发式合并+线段树合并

    将问题转化为统计以 $i$ 结尾的 $AA$ 串个数.    

    我们将后缀树建出来,然后按照启发式合并的方式每次合并两个 $endpos$ 集合.    

    那么就有:$[x-mx,x-1] ightarrow x$ 与 $x ightarrow [x+1,x+mx]$ 的贡献.    

    统计第一种贡献的话在线段树上区间查询就行.   

    第二种贡献的话要在线段树上维护一个 lazy 标记(这个标记区别于线段树合并中的 $sum$)     

    然后将整颗后缀树合并完了后统计下传根节点的 $lazy$ 标记即可.   

    细节比较多. 

    code: 

    // NOI2016 优秀的拆分     
    #include <cstdio>  
    #include <map> 
    #include <vector> 
    #include <cstring> 
    #include <string>
    #include <algorithm>    
    #define N 30002 
    #define ll long long 
    #define setIO(s) freopen(s".in","r",stdin) 
    using namespace std;   
    char S[N];  
    int n,bu[N],A[N],B[N];             
    namespace seg 
    {    
        #define lson s[x].ls 
        #define rson s[x].rs 
        int tot;         
        struct data 
        { 
            int ls,rs,sum,tag;           
            void clr() { ls=rs=sum=tag=0; }     
        }s[N*50];     
        int newnode() { return ++tot; }                       
        void pushup(int x) { s[x].sum=s[lson].sum+s[rson].sum; }              
        void mark(int x,int v) { s[x].tag+=v; }     
        void pushdown(int x) 
        {            
            if(s[x].tag) 
            {
                if(lson) mark(lson,s[x].tag); 
                if(rson) mark(rson,s[x].tag);       
                s[x].tag=0;      
            }
        }
        void update(int &x,int l,int r,int p,int v) 
        {                    
            if(!x) x=newnode();     
            s[x].sum+=v;        
            if(l==r) return;  
            pushdown(x);   
            int mid=(l+r)>>1;   
            if(p<=mid) update(lson,l,mid,p,v); 
            else update(rson,mid+1,r,p,v);    
        }      
        int query(int x,int l,int r,int L,int R) 
        { 
            if(!x||l>R||r<L||L>R) return 0;  
            if(l>=L&&r<=R) return s[x].sum;       
            int mid=(l+r)>>1,re=0;          
            pushdown(x);    
            if(L<=mid)  re+=query(lson,l,mid,L,R); 
            if(R>mid)   re+=query(rson,mid+1,r,L,R);  
            return re;  
        }          
        void add(int x,int l,int r,int L,int R,int v) 
        {
            if(!x||r<L||l>R||L>R) return;      
            if(l>=L&&r<=R) { mark(x,v); return; }  
            int mid=(l+r)>>1;   
            pushdown(x);     
            if(L<=mid)  add(lson,l,mid,L,R,v);   
            if(R>mid)   add(rson,mid+1,r,L,R,v);  
        }      
        int merge(int x,int y) 
        {
            if(!x||!y) return x+y;     
            pushdown(x); 
            pushdown(y);  
            int now=newnode();     
            s[now].sum=s[x].sum+s[y].sum;   
            s[now].tag=s[x].tag+s[y].tag;     
            s[now].ls=merge(s[x].ls,s[y].ls); 
            s[now].rs=merge(s[x].rs,s[y].rs); 
            return now;     
        }
        void dfs(int x,int l,int r) 
        {
            if(l==r) { bu[l]+=s[x].tag; return; }             
            pushdown(x); 
            int mid=(l+r)>>1;      
            if(lson) dfs(lson,l,mid); 
            if(rson) dfs(rson,mid+1,r);   
        }
        void clr() 
        {
            int i,j;              
            for(i=1;i<=tot;++i) s[i].clr();        
            tot=0;     
        }
        #undef lson 
        #undef rson 
    };   
    struct Solve 
    {                
        int tot,last;              
        vector<int>G[N<<1];     
        int pre[N<<1],mx[N<<1],ch[N<<1][26],rt[N<<1],tax[N<<1],rk[N<<1],id[N<<1];     
        void init() { last=tot=1; }         
        void extend(int c,int pos) 
        {
            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;    
                    pre[nq]=pre[q],pre[q]=pre[np]=nq;      
                    memcpy(ch[nq],ch[q],sizeof(ch[q]));      
                    for(;p&&ch[p][c]==q;p=pre[p]) ch[p][c]=nq;    
                }
            }          
            seg::update(rt[np],1,n,pos,1);  
            G[np].push_back(pos);       
        }              
        void clr() 
        {        
            int i,j; 
            seg::clr();        
            for(i=1;i<=tot;++i) 
            {
                G[i].clear(); 
                rt[i]=mx[i]=pre[i]=tax[i]=id[i]=rk[i]=0;      
                memset(ch[i],0,sizeof(ch[i]));     
            }
            tot=0;   
        } 
        void work() 
        {
            int i,j;      
            init();   
            for(i=1;i<=n;++i)   extend(S[i]-'a',i);    
            for(i=1;i<=tot;++i) ++tax[mx[i]];    
            for(i=1;i<=tot;++i) tax[i]+=tax[i-1];  
            for(i=1;i<=tot;++i) rk[tax[mx[i]]--]=i;    
            for(i=1;i<=tot;++i) id[i]=i;        
            for(i=tot;i>1;--i) 
            {
                int u=rk[i]; 
                int ff=pre[u];                  
                int a=u,b=ff;          
                if(G[id[a]].size()>G[id[b]].size()) swap(a,b);       
                for(j=0;j<G[id[a]].size();++j) 
                {
                    int x=G[id[a]][j];                
                    G[id[b]].push_back(x);            
                    if(mx[ff]==0) continue;    
                    bu[x]+=seg::query(rt[b],1,n,max(1,x-mx[ff]),x-1);                  
                    if(x<n) 
                    {   
                        seg::add(rt[b],1,n,x+1,min(n,x+mx[ff]),1);                     
                    }
                }         
                id[ff]=id[b];                             
                rt[ff]=seg::merge(rt[u],rt[ff]);        
            }
            seg::dfs(rt[1],1,n);       
            clr();    
        }  
    }suf,pre;      
    void work() 
    {      
        int i,j;      
        scanf("%s",S+1),n=strlen(S+1);                           
        pre.work();              
        for(i=1;i<=n;++i) A[i]=bu[i];    
        for(i=1;i<=n;++i) bu[i]=0;     
        for(i=1;n-i+1>i;++i) swap(S[i],S[n-i+1]);        
        suf.work();         
        for(i=1;i<=n;++i) B[i]=bu[i];    
        ll ans=0; 
        for(i=1;i<=n;++i) ans+=(ll)A[i]*B[n-i];    
        for(i=1;i<=n;++i) bu[i]=0;      
        printf("%lld
    ",ans);   
    }
    int main() 
    {        
        int i,j,T;    
        // setIO("input");               
        scanf("%d",&T); 
        while(T--) work(); 
        return 0;
    }
    

      

  • 相关阅读:
    bzoj4563: [Haoi2016]放棋子(错排+高精)
    bzoj1089 [SCOI2003]严格n元树(dp+高精)
    9.15NOIP模拟题
    洛谷 P2010 回文日期 题解
    洛谷 P1147 连续自然数和 题解
    洛谷 P1152 欢乐的跳 题解
    信息学奥赛一本通 高手训练1 统计方案数
    想学习找不到好的博客?看这里>>
    信息学奥赛一本通 高手训练1 游戏通关
    洛谷 P3398 仓鼠找sugar 题解
  • 原文地址:https://www.cnblogs.com/guangheli/p/12299691.html
Copyright © 2011-2022 走看看