[bzoj3162]独钓寒江雪_树hash_树形dp

独钓寒江雪

题目链接https://www.lydsy.com/JudgeOnline/problem.php?id=3162


题解

首先,如果没有那个本质相同的限制这就是个傻逼题。

直接树形dp就好。

那么如果加上那个限制呢?

我们发现,无论最后怎么本质相同,树的重心一定不变。

故此,从重心开始去重即可。

参考:https://www.cnblogs.com/zhoushuyu/p/9295759.html

代码

#include <bits/stdc++.h>

#define N 500010 

using namespace std;

const int mod = 1000000007 ;

char *p1, *p2, buf[100000];

typedef unsigned long long ll;

ll bs1 = 20021214 ;

ll bs2 = 20030315 ;

#define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )

int rd() {
    int x = 0;
    char c = nc();
    while (c < 48) {
        c = nc();
    }
    while (c > 47) {
        x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
    }
    return x;
}

int n, inv[N], to[N << 1], nxt[N << 1], head[N], tot, sz[N], w[N], root, rt1, rt2, flag, f[2][N], tmp[N];

ll hs[N];

inline void add(int x, int y) {
    to[ ++ tot] = y;
    nxt[tot] = head[x];
    head[x] = tot;
}

void getroot(int p, int fa) {
    sz[p] = 1;
    w[p] = 0;
    for (int i = head[p]; i; i = nxt[i]) {
        if(to[i] != fa) {
            getroot(to[i], p);
            sz[p] += sz[to[i]];
            w[p] = max(w[p], sz[to[i]]);
        }
    }
    w[p] = max(w[p], n - sz[p]);
    if (w[p] < w[root]) {
        root = p;
    }
}

int C(int n, int m) {
    int re = 1;
    for (int i = n - m + 1; i <= n; i ++ ) {
        re = (ll)re * i % mod;
    }
    for (int i = 1; i <= m; i ++ ) {
        re = (ll)re * inv[i] % mod;
    }
    return re;
}

inline bool cmp(int i, int j) {
    return hs[i] < hs[j];
}

void dfs(int p, int fa) {
    sz[p] = f[0][p] = f[1][p] = 1;
    for (int i = head[p]; i; i = nxt[i]) {
        if (to[i] != fa) {
            dfs(to[i], p);
            sz[p] += sz[to[i]];
        }
    }
    int len = 0;
    for (int i = head[p]; i; i = nxt[i]) {
        if (to[i] != fa) {
            tmp[ ++ len] = to[i];
        }
    }
    sort(tmp + 1, tmp + len + 1, cmp);
    for (int i = 1, j = 1; i <= len; i = j) {
        while (j <= len && hs[tmp[j]] == hs[tmp[i]]) {
            j ++ ;
        }
        f[0][p] = (ll)f[0][p] * C(f[0][tmp[i]] + f[1][tmp[i]] + j - i - 1, j - i) % mod;
        f[1][p] = (ll)f[1][p] * C(f[0][tmp[i]] + j - i - 1, j - i) % mod;
    }
    hs[p] = bs2 * len + sz[p];
    for (int i = 1; i <= len; i ++ ) {
        hs[p] = (hs[p] * bs1) ^ hs[tmp[i]];
    }
}

int main() {
    n = rd();
    inv[0] = inv[1] = 1;
    for (int i = 2; i <= n; i ++ ) {
        inv[i] = (ll)inv[mod % i] * (mod - mod / i) % mod;
    }
    for (int i = 1; i < n; i ++ ) {
        int x = rd(), y = rd();
        add(x, y), add(y, x);
    }
    w[0] = n;
    getroot(1, 0);
    getroot(root, 0);
    for (int i = head[root], last = 0; i; last = i, i = nxt[i]) {
        if (sz[to[i]] * 2 == n) {
            n ++ ;
            if (i == head[root]) {
                head[root] = nxt[i];
            }
            else {
                nxt[last] = nxt[i];
            }
            for (int j = head[to[i]], lst = 0; j; lst = j, j = nxt[j]) {
                if (to[j] == root) {
                    if (j == head[to[i]]) {
                        head[to[i]] = nxt[j];
                    }
                    else {
                        nxt[lst] = nxt[j];
                    }
                    break;
                }
            }
            add(n, root);
            add(root, n);
            add(n, to[i]);
            add(to[i], n);
            rt1 = root;
            rt2 = to[i];
            root = n;
            flag = 1;
            break;
        }
    }
    dfs(root, 0);
    if (!flag) {
        cout << (f[0][root] + f[1][root]) % mod << endl ;
    }
    else if (hs[rt1] == hs[rt2]) {
        cout << (f[0][root] - C(f[1][rt1] + 1, 2) + mod) % mod;
    }
    else {
        cout << ((ll)f[0][rt1] * f[0][rt2] + (ll)f[0][rt1] * f[1][rt2] + (ll)f[1][rt1] * f[0][rt2]) % mod << endl ;
    }
}

小结:树hash的时候,如果一个模数担心过不去,像我一样开两个就好了233

posted @ 2019-08-27 22:35  JZYshuraK_彧  阅读(230)  评论(0编辑  收藏  举报