1. 程式人生 > >P3346 [ZJOI2015]諸神眷顧的幻想鄉

P3346 [ZJOI2015]諸神眷顧的幻想鄉

輸入格式 直接 特殊 vector 生日 輸出 後綴自動機 組織 太陽

$ \color{#0066ff}{ 題目描述 }$

幽香是全幻想鄉裏最受人歡迎的萌妹子,這天,是幽香的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個。

\(\color{#0066ff}{輸入格式}\)

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

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

\(\color{#0066ff}{輸出格式}\)

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

\(\color{#0066ff}{輸入樣例}\)

7 3
0 2 1 2 1 0 0
1 2
3 4
3 5
4 6
5 7
2 5

\(\color{#0066ff}{輸出樣例}\)

30

\(\color{#0066ff}{數據範圍與提示}\)

對於所有數據,1<=n<=100000, 1<=c<=10。

對於15%的數據,n<=2000。

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

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

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

\(\color{#0066ff}{題解}\)

顯然題目是找不同字串個數,如果給我們的樹是一條鏈,那麽直接SAM就水了

現在是一棵樹,如果用後綴自動機的話,怎麽把所有的極長鏈都扔進去呢?

不難發現, 極長鏈的兩端一定度為1,題目還告訴你度為1的點不超過20,就可以暴力了

暴力把所有的極長鏈正反搜出來,加入SAM,統計就行了

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 2e6 + 100;
struct node {
    node *ch[13], *fa;
    int len, siz;
    node(int len = 0, int siz = 0): len(len), siz(siz) {
        fa = NULL; memset(ch, 0, sizeof ch); 
    }
}pool[maxn], *tail, *root;
node *extend(int c, node *lst) {
    node *o = new(tail++) node(lst->len + 1, 1), *v = lst;
    for(; v && !v->ch[c]; v = v->fa) v->ch[c] = o;
    if(!v) o->fa = root;    
    else if(v->len + 1 == v->ch[c]->len) o->fa = v->ch[c];
    else {
        node *n = new(tail++) node(v->len + 1), *d = v->ch[c];
        std::copy(d->ch, d->ch + 13, n->ch);
        n->fa = d->fa, d->fa = o->fa = n;
        for(; v && v->ch[c] == d; v = v->fa) v->ch[c] = n;
    }
    return o;
}
void init() {
    tail = pool;
    root = new(tail++) node();
}
int du[maxn], n, c, val[maxn];
std::vector<int> G[maxn];
void dfs(int x, int fa, node *last) {
    node *now = extend(val[x], last);
    for(int to : G[x]) if(to != fa) dfs(to, x, now);
}
int main() {
    n = in(), c = in();
    for(int i = 1; i <= n; i++) val[i] = in();
    int x, y;
    for(int i = 1; i < n; i++) {
        du[x = in()]++, du[y = in()]++;
        G[x].push_back(y), G[y].push_back(x);
    }
    init();
    for(int i = 1; i <= n; i++) if(du[i] == 1) dfs(i, 0, root);
    LL ans = 0;
    for(node *o = pool + 1; o != tail; o++) ans += o->len - o->fa->len;
    printf("%lld\n", ans);
    return 0;
}

P3346 [ZJOI2015]諸神眷顧的幻想鄉