zoukankan      html  css  js  c++  java
  • 树 题解

    一、题目:


    二、思路:

    先来简要介绍一下部分分。

    第一组20分,dfs爆搜。


    第二组20分,是一个菊花图。(在这里顺便提一句,在《组合数学》这本书上,菊花图被称作“星”,非常得有意思。)很显然我们需要分类讨论:

    1. 根节点不是关键点。答案:(sumlimits_{i=1}^m S(K,i))
    2. 根节点是关键点。答案:(sumlimits_{i=1}^{m}S(K-1,i-1))

    其中,(S) 是第二类斯特林数。其实上面这两个和式都是贝尔数。


    第三组60分。我们需要认真考虑一下。首先需要建出虚树来,在虚树上做树形DP。

    设DP状态 (dp[i,x,j]) 为考虑了 (x) 的前 (i) 个儿子,分了 (j) 组的方案数。于是有 DP方程

    [dp[i,x,j]=sumlimits_{j_1,j_2}dp[i-1,x,j_1] imes dp[y,j_2] imesinom{j_1}{j_1+j_2-j} imesinom{j_2}{j_1+j_2-j} imes(j_1+j_2-j)! ]

    (y)(x) 的第 (i) 个儿子。

    时间复杂度 (O(n imes m^3))。但肯定跑不满。


    第四组满分。我们发现这道题很像第二类斯特林数的模型。那么我们就需要考虑一下如何将斯特林数的模型运用到这道题上。

    (g_x)(x) 到根的路径中有多少个关键点。如果我们把关键点按照 (g) 排序,那么得到的序列就满足一个性质:(x) 的所有祖先全部排在 (x) 的前面。于是类比第二类斯特林数,我们设 (S'(i,j)) 为考虑了前 (i) 个关键点,分成不标号的 (j) 组的方案数。于是有

    [S'(i,j)=S'(i-1,j-1)+S'(i-1,j) imes max{j - g_x,0} ]

    其中,(x) 是第 (i) 个关键点。

    时间复杂度 (O(nm))

    三、代码:

    60分

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    #define FILEIN(s) freopen(s".in", "r", stdin);
    #define FILEOUT(s) freopen(s".out", "w", stdout)
    #define mem(s, v) memset(s, v, sizeof s)
    
    inline int read(void) {
        int x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return f * x;
    }
    
    const int maxn = 1e4 + 5, logmaxn = 15;
    const int maxm = 55, mod = 1e9 + 7;
    
    int n, Q;
    int dfn[maxn], num, fa[maxn][logmaxn], dep[maxn], K, m;
    int query[maxn], stk[maxn], top, cnt[maxn];
    long long dp[maxn][maxm], tmp[maxm], factor[maxm], C[maxm][maxm];
    bool tag[maxn];
    
    struct Graph {
        int head[maxn], tot;
        struct Edge {
            int y, next;
            Edge() {}
            Edge(int _y, int _next) : y(_y), next(_next) {}
        }e[maxn << 1];
        inline void connect(int x, int y) {
            e[++ tot] = Edge(y, head[x]);
            head[x] = tot;
        }
    }A, B;
    
    inline bool cmp(const int &a, const int &b) {
        return dfn[a] < dfn[b];
    }
    
    void prework(int x, int father) {
        fa[x][0] = father;
        for (int i = 1; i <= 14; ++ i) 
            fa[x][i] = fa[fa[x][i - 1]][i - 1];
        dep[x] = dep[father] + 1;
        dfn[x] = ++ num;
        for (int i = A.head[x]; i; i = A.e[i].next) {
            int y = A.e[i].y;
            if (y == father) continue;
            prework(y, x);
        }
    }
    
    void init(void) {
        factor[0] = 1;
        for (int i = 1; i <= 50; ++ i) 
            factor[i] = factor[i - 1] * i % mod;
        C[0][0] = 1;
        for (int i = 1; i <= 50; ++ i) {
            C[i][0] = 1;
            for (int j = 1; j <= i; ++ j) 
                C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
    
    inline int LCA(int x, int y) {
        if (dep[x] < dep[y]) swap(x, y);
        for (int i = 14; i >= 0; -- i)
            if (dep[fa[x][i]] >= dep[y]) 
                x = fa[x][i];
        if (x == y) return x;
        for (int i = 14; i >= 0; -- i)
            if (fa[x][i] != fa[y][i])
                x = fa[x][i], y = fa[y][i];
        return fa[x][0];
    }
    
    void insert(int x) {
        if (x == 1) return;
        if (top <= 1) { stk[++ top] = x; return; }
        int lca = LCA(stk[top], x);
        if (lca == stk[top]) { stk[++ top] = x; return; }
        while (top > 1 && dfn[lca] <= dfn[stk[top - 1]]) {
            B.connect(stk[top - 1], stk[top]);
            B.connect(stk[top], stk[top - 1]);
            -- top;
        }
        if (lca != stk[top]) {
            B.connect(lca, stk[top]);
            B.connect(stk[top], lca);
            stk[top] = lca;
        }
        stk[++ top] = x;
    }
    
    void dfs(int x, int father) {
        for (int i = B.head[x]; i; i = B.e[i].next) {
            int y = B.e[i].y;
            if (y == father) continue;
            dfs(y, x);
        }
    
        dp[x][0] = 1;
        for (int i = B.head[x]; i; i = B.e[i].next) {
            int y = B.e[i].y;
            if (y == father) continue;
            swap(tmp, dp[x]);
    
            mem(dp[x], 0);
            for (int j = 0; j <= min(m, cnt[x] + cnt[y]); ++ j) 
                for (int j1 = 0; j1 <= min(j, min(m, cnt[x])); ++ j1) 
                    for (int j2 = j - j1; j2 <= min(j, min(m, cnt[y])); ++ j2) 
                        (dp[x][j] += tmp[j1] * dp[y][j2] % mod * C[j1][j1 + j2 - j] % mod * C[j2][j1 + j2 - j] % mod * factor[j1 + j2 - j] % mod) %= mod;
    
            cnt[x] += cnt[y];
        }
        if (tag[x]) {
            ++ cnt[x];
            for (int i = min(cnt[x], m); i >= 1; -- i) dp[x][i] = dp[x][i - 1];
            dp[x][0] = 0;
        }
    } 
    
    void clear(int x, int father) {
        for (int i = B.head[x]; i; i = B.e[i].next) {
            int y = B.e[i].y;
            if (y == father) continue;
            clear(y, x);
        }
        tag[x] = false;
        cnt[x] = 0;
        for (int i = 1; i <= m; ++ i) dp[x][i] = 0;
        B.head[x] = 0;
    }
    
    int main() {
        FILEIN("tree"); FILEOUT("tree");
        n = read(); Q = read();
        for (int i = 1; i < n; ++ i) {
            int x = read(), y = read();
            A.connect(x, y); A.connect(y, x);
        }
        prework(1, 0);
        init();
        while (Q --) {
            K = read(); m = read();
            int r = read();
            for (int i = 1; i <= K; ++ i)   
                tag[query[i] = read()] = true;
            if (!tag[r]) query[++ K] = r;
            sort(query + 1, query + K + 1, cmp);
            stk[++ top] = 1;
            for (int i = 1; i <= K; ++ i)
                insert(query[i]);
            while (top > 1) {
                B.connect(stk[top - 1], stk[top]);
                B.connect(stk[top], stk[top - 1]);
                -- top;
            }
            dfs(r, 0);
            long long ans = 0;
            for (int i = 0; i <= m; ++ i) (ans += dp[r][i]) %= mod;
            printf("%lld
    ", ans);
    
            top = 0; B.tot = 0;
            clear(r, 0);
        }
        return 0;
    }
    

    100分

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    #define FILEIN(s) freopen(s".in", "r", stdin);
    #define FILEOUT(s) freopen(s".out", "w", stdout)
    #define mem(s, v) memset(s, v, sizeof s)
    
    inline int read(void) {
        int x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return f * x;
    }
    
    const int maxn = 1e5 + 5, maxm = 305, logmaxn = 20, mod = 1e9 + 7;
    int dfn[maxn], fa[maxn][logmaxn], dep[maxn], num;
    int stk[maxn], top, n, Q, K, m, query[maxn], g[maxn], tmp[maxn];
    long long S[maxn][maxm];
    bool tag[maxn];
    
    struct Graph {
        int head[maxn], tot;
        struct Edge {
            int y, next;
            Edge() {}
            Edge(int _y, int _next) : y(_y), next(_next) {}
        }e[maxn << 1];
        inline void connect(int x, int y) {
            e[++ tot] = Edge(y, head[x]);
            head[x] = tot;
        }
    }A, B;
    
    inline bool cmp(const int &a, const int &b) {
        return dfn[a] < dfn[b];
    }
    
    inline bool cmp2(const int &a, const int &b) {
        return g[a] < g[b];
    }
    
    void prework(int x, int father) {
        fa[x][0] = father;
        for (int i = 1; i <= 17; ++ i) 
            fa[x][i] = fa[fa[x][i - 1]][i - 1];
        dep[x] = dep[father] + 1;
        dfn[x] = ++ num;
        for (int i = A.head[x]; i; i = A.e[i].next) {
            int y = A.e[i].y;
            if (y == father) continue;
            prework(y, x);
        }
    }
    
    inline int LCA(int x, int y) {
        if (dep[x] < dep[y]) swap(x, y);
        for (int i = 17; i >= 0; -- i)
            if (dep[fa[x][i]] >= dep[y]) 
                x = fa[x][i];
        if (x == y) return x;
        for (int i = 17; i >= 0; -- i)
            if (fa[x][i] != fa[y][i])
                x = fa[x][i], y = fa[y][i];
        return fa[x][0];
    }
    
    void insert(int x) {
        if (x == 1) return;
        if (top <= 1) { stk[++ top] = x; return; }
        int lca = LCA(stk[top], x);
        if (lca == stk[top]) { stk[++ top] = x; return; }
        while (top > 1 && dfn[lca] <= dfn[stk[top - 1]]) {
            B.connect(stk[top - 1], stk[top]);
            B.connect(stk[top], stk[top - 1]);
            -- top;
        }
        if (lca != stk[top]) {
            B.connect(lca, stk[top]);
            B.connect(stk[top], lca);
            stk[top] = lca;
        }
        stk[++ top] = x;
    }
    
    void dfs(int x, int father) {
        g[x] = g[father] + tag[father];
        for (int i = B.head[x]; i; i = B.e[i].next) {
            int y = B.e[i].y;
            if (y == father) continue;
            dfs(y, x);
        }
    }
    
    void clear(int x, int father) {
        for (int i = B.head[x]; i; i = B.e[i].next) {
            int y = B.e[i].y;
            if (y == father) continue;
            clear(y, x);
        }
        tag[x] = g[x] = B.head[x] = 0;
    }
    
    int main() {
        FILEIN("tree"); FILEOUT("tree");
        n = read(); Q = read();
        for (int i = 1; i < n; ++ i) {
            int x = read(), y = read();
            A.connect(x, y); A.connect(y, x);
        }
        prework(1, 0);
        while (Q --) {
            K = read(); m = read();
            int r = read();
            for (int i = 1; i <= K; ++ i) 
                tag[tmp[i] = query[i] = read()] = true;
            if (!tag[r]) tmp[++ K] = r;
            sort(tmp + 1, tmp + K + 1, cmp);
            stk[++ top] = 1;
            for (int i = 1; i <= K; ++ i)
                insert(tmp[i]);
            while (top > 1) {
                B.connect(stk[top - 1], stk[top]);
                B.connect(stk[top], stk[top - 1]);
                -- top;
            }
            dfs(r, 0);
            if (!tag[r]) -- K;
            sort(query + 1, query + K + 1, cmp2);
    
            S[0][0] = 1;
            for (int i = 1; i <= K; ++ i) 
                for (int j = 1; j <= min(m, i); ++ j) 
                    S[i][j] = (S[i - 1][j - 1] + S[i - 1][j] * max(0, j - g[query[i]]) % mod) % mod;
                
            long long ans = 0;
            for (int i = 0; i <= m; ++ i) 
                (ans += S[K][i]) %= mod;
            printf("%lld
    ", ans);
    
            B.tot = 0;
            top = 0;
            clear(r, 0);
        }
        return 0;
    }
    
  • 相关阅读:
    jenkins中通过Publish Over SSH将项目部署到远程机器上
    GitHub 开源的 MySQL 在线更改 Schema 工具【转】
    MySQL ProxySQL读写分离实践
    MySQL ProxySQL读写分离使用初探
    LVS+Keepalived实现DBProxy的高可用
    Redis Codis 部署安装
    CS 和 BS 的区别和优缺点(转)
    Linux的SOCKET编程详解(转)
    数据结构-深度遍历和广度遍历(转)
    社保相关
  • 原文地址:https://www.cnblogs.com/little-aztl/p/14814326.html
Copyright © 2011-2022 走看看