码迷,mamicode.com
首页 > 其他好文 > 详细

[ZJOI2015]诸神眷顾的幻想乡

时间:2018-12-12 17:38:50      阅读:185      评论:0      收藏:0      [点我收藏+]

标签:oid   重复   位置   nbsp   gis   amp   col   space   etc   

求不同的子串个数

如果规定根的话,那么弯曲的路径难以处理。

由于只有20个叶子,所以以每个叶子分别为根,建20棵trie树,再把20棵trie树合成一棵。

这样,trie上一个到某个祖先的路径构成了所有的子串。(可能重复)

所以trie上建SAM。SAM的路径条数(或者每个点的len[i]-len[fa[i]])就是本质不同的子串个数

 

至于trie上建SAM,可以dfs,或者bfs建。

只要记录树上x的father的SAM上的节点编号p,从这个p作为nd开始建即可。

技术分享图片
#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^‘0‘)
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch==-)&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=100000+5;
int n,m;
int clo[N];
int du[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;
}
struct trie{
    int cnt,ch[20*N][10];
    trie(){
        cnt=1;
    }
}tr;
void dfs(int x,int y,int fa){
    if(!tr.ch[y][clo[x]]){
        tr.ch[y][clo[x]]=++tr.cnt;
    }
    y=tr.ch[y][clo[x]];
    for(reg i=hd[x];i;i=e[i].nxt){
        int z=e[i].to;
        if(z==fa) continue;
        dfs(z,y,x);
    }
}
struct SAM{
    int fa[20*N],len[20*N],ch[20*N][10],cnt;
    ll s[20*N];
    SAM(){
        cnt=1;
    }
    int ins(int c,int p){
        //cout<<" c p "<<c<<" "<<p<<endl;
        int np=++cnt;len[np]=len[p]+1;
        while(p&&ch[p][c]==0) ch[p][c]=np,p=fa[p];
        if(!p){
            fa[np]=1;return np;
        }
        int q=ch[p][c];
        if(len[q]==len[p]+1){
            fa[np]=q;return np;
        }
        len[++cnt]=len[p]+1;
        fa[cnt]=fa[q];fa[q]=fa[np]=cnt;
        for(reg i=0;i<m;++i) ch[cnt][i]=ch[q][i];
        while(p&&ch[p][c]==q) ch[p][c]=cnt,p=fa[p];
        return np;
    }
    ll dfs(int x){
        if(s[x]) return s[x];
        s[x]=1;
        for(reg i=0;i<m;++i){
            if(ch[x][i]) {
                s[x]+=dfs(ch[x][i]);
            }
        }
        return s[x];
    }
}sam;
int pos[20*N];
void build(int x){
    for(reg i=0;i<m;++i){
        if(tr.ch[x][i]){
            pos[tr.ch[x][i]]=sam.ins(i,pos[x]);
            build(tr.ch[x][i]);
        }
    }
}


int main(){
    rd(n);rd(m);
    for(reg i=1;i<=n;++i) rd(clo[i]);
    int x,y;
    for(reg i=1;i<=n-1;++i){
        rd(x);rd(y);
        add(x,y);add(y,x);
        ++du[x];++du[y];
    }
    for(reg i=1;i<=n;++i){
        if(du[i]==1){
            //cout<<" st "<<i<<endl;
            dfs(i,1,0);
        }
    }
    //cout<<" trie‘s cnt "<<tr.cnt<<endl;
    pos[1]=1;
    build(1);
    printf("%lld\n",sam.dfs(1)-1);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/12 11:22:24
*/
1

其实不用建出合并的trie。

直接在20次dfs的时候(每次dfs都是一个小trie),20次每次从根开始插入即可。

重复的前缀部分会和根节点隔离开的。既不会能从根节点到达,而且fa[i]=len[i],没有包含一个子串。

技术分享图片
#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^‘0‘)
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch==-)&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=100010;
#define TOT 2000000
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
int n,m;
int clo[N];
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
struct SAM{
    int fa[TOT],len[TOT],ch[TOT][10],cnt;
    ll s[TOT];
    SAM(){
        cnt=1;
    }
    int ins(int c,int p){
        //cout<<" c p "<<c<<" "<<p<<endl;
        int np=++cnt;len[np]=len[p]+1;
        while(p&&ch[p][c]==0) ch[p][c]=np,p=fa[p];
        if(!p){
            fa[np]=1;return np;
        }
        int q=ch[p][c];
        if(len[q]==len[p]+1){
            fa[np]=q;return np;
        }
        len[++cnt]=len[p]+1;
        fa[cnt]=fa[q];fa[q]=fa[np]=cnt;
        for(reg i=0;i<m;++i) ch[cnt][i]=ch[q][i];
        while(p&&ch[p][c]==q) ch[p][c]=cnt,p=fa[p];
        return np;
    }
    ll dfs(int x){
        if(s[x]) return s[x];
        s[x]=1;
        for(reg i=0;i<m;++i){
            if(ch[x][i]) {
                s[x]+=dfs(ch[x][i]);
            }
        }
        return s[x];
    }
}sam;
int du[N];
void dfs(int x,int fa,int p){
    p=sam.ins(clo[x],p);
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x,p);
    }
}
int main(){
    rd(n);rd(m);
    for(reg i=1;i<=n;++i) rd(clo[i]);
    int x,y;
    for(reg i=1;i<=n-1;++i){
        rd(x);rd(y);
        add(x,y);add(y,x);
        du[x]++;du[y]++;
    }
    for(reg i=1;i<=n;++i){
        if(du[i]==1) dfs(i,0,1);
    }
    //cout<<sam.cnt<<endl;
    printf("%lld",sam.dfs(1)-1);
    return 0;
} 

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/12 15:07:01
*/
2

 

(当然,如果广义SAM要处理Right集合的大小的话,要根据建造的方法处理

1.所有的串依次从rt=1开始插入

叶子sz赋值为1,然后Parent树上往上push。注意,不管节点是否有实际意义,都要push上去。因为代表一个出现的位置

2.trie树

先要找到trie树某个点的sz[x],然后插入的时候sz就是这个sz[x]

 

[ZJOI2015]诸神眷顾的幻想乡

标签:oid   重复   位置   nbsp   gis   amp   col   space   etc   

原文地址:https://www.cnblogs.com/Miracevin/p/10109139.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!