@bzoj - 3162@ 獨釣寒江雪


@description@

求一棵無根樹上本質不一樣的獨立集的個數 mod 10^9 + 7。spa

咱們稱兩個獨立集 A, B 是不一樣的,當前僅當:
(1)存在一種方案,將樹中的結點從新標號後,在 A 中出現的任意一條邊在 B 中也應該出現。
(2)在知足條件(1)的前提下,以一樣的重標號方式,若是 x 在 A 中屬於獨立集,在 B 中也應該屬於獨立集。code

輸入格式
第一行有一個正整數 n,表示結點數。結點從 1 編號到 n。
接下來 n - 1 行,每行兩個整數 v, u,表示 v,u 兩點之間有一條邊。
輸出格式
輸出一行表示答案 mod 10^9 + 7。ip

樣例輸入1
1
樣例輸出1
2
樣例輸入2
6
1 2
1 3
1 4
4 5
4 6
樣例輸出2
9get

數據範圍與約定
對於 100% 的數據,n <= 500000。it

@solution@

若是沒有本質相同的要求,直接樹形 dp 就完事兒了。io

問題說的是無根樹,咱們能夠轉成更方便的有根樹來作(包括樹形 dp 也是在有根樹上作)。class

記點 rt 爲根。
若是在從新標號後 rt 的位置不變,那麼依據題目所說,與 rt 相連的點依然與 rt 相連。也就是說,咱們只是將 rt 的子樹重排,而且只會把同構的子樹互相換位置。
同理對於這棵有根樹中的每一個點,咱們都只會改變它同構的子樹之間的順序,而不會破壞父子關係。stream

在以上條件知足的狀況下,咱們就能夠進行計數了。
仍是使用 dp,只是 dp 的時候咱們對於重構的子樹總體轉移(用樹哈希判一下重構便可)。重構

假如對於某一類子樹共 x 個,這一類子樹的方案數爲 k。它的貢獻爲可重集的組合 C(x+k-1, x)。
至關於方程 p1 + p2 + ... + pk = x 的解,其中 pi 表示選擇第 i 種方案的子樹個數。
注意這裏的 k 是取餘事後的,可是能夠運用 lucas 證實這裏的取模不會影響最終結果。
這裏的組合數能夠直接暴算,x 的總和爲 O(n)。

假如從新編號後,rt 的位置變到了另外一個結點。能夠發現這種狀況下變化很大,不是很好計數。

那麼怎麼才能恰當地選擇 rt,使得 rt 不可能改變到另外一個結點呢?
注意到假如點 x 被重編號成 rt,那麼以點 x 爲根與以點 rt 爲根獲得的有根樹應該是同構的。
能夠聯想到有關樹同構的另外一個套路:重心。

重心做爲整棵樹中的特徵點,只會在有兩個重心的時候可能與另外一個重心同構。
假若有兩個重心,它們必然相鄰。咱們能夠在重心之間插入一個虛點(但其實不用實際插入一個虛點),最後特判一下虛點就行了。這個虛點就成了惟一的重心。
那麼以重心爲根,就能夠保證根不會被重編號成另外的數。

@accepted code@

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
typedef pair<ull, int> pr;
const int MAXN = 500000;
const int MOD = int(1E9) + 7;
int inv[MAXN + 5];
inline int add(int a, int b) {return (a + b) % MOD;}
inline int mul(int a, int b) {return 1LL*a*b % MOD;}
inline int sub(int a, int b) {return add(a, MOD-b);}
inline int dv(int a, int b) {return mul(a, inv[b]);}
struct edge{
    int to; edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=edges;
void addedge(int u, int v) {
    edge *p = (++ecnt);
    p->to = v, p->nxt = adj[u], adj[u] = p;
    p = (++ecnt);
    p->to = u, p->nxt = adj[v], adj[v] = p;
}
int siz[MAXN + 5], fa[MAXN + 5], n;
int get_siz(int x, int f) {
    fa[x] = f, siz[x] = 1;
    for(edge *p=adj[x];p;p=p->nxt)
        if( p->to != f )
            siz[x] += get_siz(p->to, x);
    return siz[x];
}
int hvy[MAXN + 5];
int getG(int x, int tot) {
    int ret = -1; hvy[x] = tot - siz[x];
    for(edge *p=adj[x];p;p=p->nxt)
        if( p->to != fa[x] ) {
            int t = getG(p->to, tot);
            if( ret == -1 || hvy[ret] > hvy[t] )
                ret = t;
            if( siz[p->to] > hvy[x] )
                hvy[x] = siz[p->to];
        }
    if( ret == -1 || hvy[ret] > hvy[x] )
        ret = x;
    return ret;
}
ull rd[MAXN + 5];
void init() {
    srand(20041112^n);
    for(int i=1;i<=n;i++)
        rd[i] = (((ull)rand() << 16 | rand()) << 16 | rand()) << 16 | rand();
    inv[1] = 1;
    for(int i=2;i<=n;i++)
        inv[i] = MOD - 1LL*inv[MOD % i]*(MOD/i)%MOD;
}
int C(int n, int m) {
    int ret = 1;
    for(int i=1;i<=m;i++)
        ret = dv(mul(ret, n + m - i), i);
    return ret;
}// C(n + m - 1, m)
vector<pair<ull, int> >v;
ull h[MAXN + 5]; int f[3][MAXN + 5];
void dfs(int x, int fa) {
    siz[x] = h[x] = 1;
    for(edge *p=adj[x];p;p=p->nxt) {
        if( p->to == fa ) continue;
        dfs(p->to, x), h[x] += rd[siz[p->to]] * h[p->to], siz[x] += siz[p->to];
    }
    v.clear();
    for(edge *p=adj[x];p;p=p->nxt) {
        if( p->to == fa ) continue;
        v.push_back(make_pair(h[p->to], p->to));
    }
    sort(v.begin(), v.end());
    f[0][x] = f[1][x] = 1;
    int lst = 0;
    for(int i=1;i<v.size();i++) {
        if( v[i].first != v[i-1].first ) {
            f[0][x] = mul(f[0][x], C(f[2][v[lst].second], i - lst));
            f[1][x] = mul(f[1][x], C(f[0][v[lst].second], i - lst));
            lst = i;
        }
    }
    if( v.size() ) {
        f[0][x] = mul(f[0][x], C(f[2][v[lst].second], v.size() - lst));
        f[1][x] = mul(f[1][x], C(f[0][v[lst].second], v.size() - lst));
    }
    f[2][x] = add(f[0][x], f[1][x]);
}
int main() {
    scanf("%d", &n), init();
    for(int i=1;i<n;i++) {
        int u, v; scanf("%d%d", &u, &v);
        addedge(u, v);
    }
    int p = getG(1, get_siz(1, 0));
    if( fa[p] && hvy[p] == hvy[fa[p]] ) {
        int q = fa[p];
        dfs(p, q), dfs(q, p);
        if( h[p] != h[q] )
            printf("%d\n", sub(mul(f[2][p], f[2][q]), mul(f[1][p], f[1][q])));
        else printf("%d\n", add(mul(f[0][p], f[1][p]), C(f[0][p], 2)));
    }
    else dfs(p, 0), printf("%d\n", f[2][p]);
}

@details@

感受看網上的題解。。。好像爲何要以重心都解釋得很簡略。。。 自閉了很久才明白爲何要以重心爲根 QAQ。。。

相關文章
相關標籤/搜索