1. 程式人生 > >【BZOJ3926】【ZJOI2015】諸神眷顧的幻想鄉 - 廣義字尾自動機

【BZOJ3926】【ZJOI2015】諸神眷顧的幻想鄉 - 廣義字尾自動機

題意:

Description

幽香是全幻想鄉里最受人歡迎的萌妹子,這天,是幽香的2600歲生日,無數幽香的粉絲到了幽香家門前的太陽花田上來為幽香慶祝生日。 

粉絲們非常熱情,自發組織表演了一系列節目給幽香看。幽香當然也非常高興啦。  這時幽香發現了一件非常有趣的事情,太陽花田有n塊空地。在過去,幽香為了方便,在這n塊空地之間修建了n-1條邊將它們連通起來。也就是說,這n塊空地形成了一個樹的結構。  有n個粉絲們來到了太陽花田上。為了表達對幽香生日的祝賀,他們選擇了c中顏色的衣服,每種顏色恰好可以用一個0到c-1之間的整數來表示。並且每個人都站在一個空地上,每個空地上也只有一個人。這樣整個太陽花田就花花綠綠了。幽香看到了,感覺也非常開心。 
粉絲們策劃的一個節目是這樣的,選中兩個粉絲A和B(A和B可以相同),然後A所在的空地到B所在的空地的路徑上的粉絲依次跳起來(包括端點),幽香就能看到一個長度為A到B之間路徑上的所有粉絲的數目(包括A和B)的顏色序列。一開始大家打算讓人一兩個粉絲(注意:A,B和B,A是不同的,他們形成的序列剛好相反,比如紅綠藍和藍綠紅)都來一次,但是有人指出這樣可能會出現一些一模一樣的顏色序列,會導致審美疲勞。  於是他們想要問題,在這個樹上,一共有多少可能的不同的顏色序列(子串)幽香可以看到呢?  太陽花田的結構比較特殊,只與一個空地相鄰的空地數量不超過20個。 

Input

 第一行兩個正整數n,c。表示空地數量和顏色數量。 

第二行有n個0到c-1之間,由空格隔開的整數,依次表示第i塊空地上的粉絲的衣服顏色。(這裡我們按照節點標號從小到大的順序依次給出每塊空地上粉絲的衣服顏色)。  接下來n-1行,每行兩個正整數u,v,表示有一條連線空地u和空地v的邊。 

Output

一行,輸出一個整數,表示答案。

HINT

對於所有資料,1<=n<=100000, 1<=c<=10。

對於15%的資料,n<=2000。

另有5%的資料,所有空地都至多與兩個空地相鄰。

另有5%的資料,除一塊空地與三個空地相鄰外,其他空地都分別至多與兩個空地相鄰。

另有5%的資料,除某兩塊空地與三個空地相鄰外,其他空地都分別至多與兩個空地相鄰。

題解:

Orz clj 陳老師神題(可能是神(du)仙(liu)ZJOI的開端?)

首先要注意到資料的一個關鍵性質:葉子節點不會超過20個,所以肯定要將題目關於葉子節點做一些轉化;

題目中的樹顯然可以看成一棵trie,把$A$到$B$路徑上的點連起來就能得到一個字串;

考慮一個在trie樹上“轉彎”的路徑,即從$u$經過$lca(u,v)$再到$v$的一條路徑,假如把整棵樹以$u$或者$v$的某棵子樹中的葉子節點作為根重構的話,就會變成一條深度嚴格遞增的“直”路徑,當然原本在trie樹上就是“直”的路徑也一樣;

綜合起來就是以每個葉子節點為根重構整棵trie,就一定能把原來trie上的每條路徑變成一條深度嚴格遞增的路徑;

顯然這樣的路徑必定是此時根節點到某個節點路徑上字串的一個字尾;

那麼直接對於所有重構出的trie建出廣義字尾自動機,要求所有不相同的子串數量,就利用字尾自動機基本常識統計答案$max[s]-max[fa[s]]$即可;

這裡實現廣義字尾自動機就在trie上dfs,然後每次把last設為父節點在廣義字尾自動機上對應的節點即可(具體見lyy2015國家集訓隊論文)

程式碼:

沒能1A差評

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 typedef double db;
12 struct edge{
13     int v,next;
14 }a[200001];
15 int n,m,u,v,tote=0,rt=1,tot=1,last=1,d[100001],c[100001],head[100001],son[2000001][11],fa[2000001],mx[2000001];
16 ll ans=0;
17 void add(int u,int v){
18     a[++tote].v=v;
19     a[tote].next=head[u];
20     head[u]=tote;
21 }
22 int extend(int p,int ch){
23     int np=++tot;
24     mx[np]=mx[p]+1;
25     for(;p&&!son[p][ch];p=fa[p])son[p][ch]=np;
26     if(!p)fa[np]=rt;
27     else{
28         int q=son[p][ch];
29         if(mx[q]==mx[p]+1)fa[np]=q;
30         else{
31             int nq=++tot;
32             mx[nq]=mx[p]+1;
33             memcpy(son[nq],son[q],sizeof(son[q]));
34             fa[nq]=fa[q];
35             fa[q]=fa[np]=nq;
36             for(;p&&son[p][ch]==q;p=fa[p])son[p][ch]=nq;
37         }
38     }
39     return last=np;
40 }
41 void dfs(int u,int fa,int p){
42     int nw=extend(p,c[u]);
43     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
44         int v=a[tmp].v;
45         if(v!=fa){
46             dfs(v,u,nw);
47         }
48     }
49 }
50 int main(){
51     memset(head,-1,sizeof(head));
52     memset(d,0,sizeof(d));
53     scanf("%d%d",&n,&m);
54     for(int i=1;i<=n;i++){
55         scanf("%d",&c[i]);
56     }
57     for(int i=1;i<n;i++){
58         scanf("%d%d",&u,&v);
59         add(u,v);
60         add(v,u);
61         d[u]++,d[v]++;
62     }
63     for(int i=1;i<=n;i++){
64         if(d[i]==1){
65             dfs(i,-1,rt);
66         }
67     }
68     for(int i=2;i<=tot;i++){
69         ans+=(mx[i]-mx[fa[i]]);
70     }
71     printf("%lld",ans);
72     return 0;
73 }