zoukankan      html  css  js  c++  java
  • 树形DP从入门到入土

    树形DP从入门到入土

    codeforces 161D

    http://codeforces.com/problemset/problem/161/D

    题意:

    给定一棵有n个节点的树和一个正整数k,找出距离恰好为k的不同节点对的数量。

    题解:

    [dp[i][j]表示点i与其他节点距离为j的节点个数 ]

    [转移:dp[u][len]+=dp[v][len-1] ]

    #include <set>
    #include <map>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> pii;
    typedef unsigned long long uLL;
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    #define bug printf("*********
    ")
    #define FIN freopen("input.txt","r",stdin);
    #define FON freopen("output.txt","w+",stdout);
    #define IO ios::sync_with_stdio(false),cin.tie(0)
    #define debug1(x) cout<<"["<<#x<<" "<<(x)<<"]
    "
    #define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]
    "
    #define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]
    "
    const int maxn = 5e4 + 5;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    const double Pi = acos(-1);
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    LL lcm(LL a, LL b) {
        return a / gcd(a, b) * b;
    }
    double dpow(double a, LL b) {
        double ans = 1.0;
        while(b) {
            if(b % 2)ans = ans * a;
            a = a * a;
            b /= 2;
        } return ans;
    }
    LL quick_pow(LL x, LL y) {
        LL ans = 1;
        while(y) {
            if(y & 1) {
                ans = ans * x % mod;
            } x = x * x % mod;
            y >>= 1;
        } return ans;
    }
    struct EDGE {
        int v, nxt;
    } edge[maxn << 1];
    int head[maxn], tot;
    void init() {
        memset(head, -1, sizeof(head));
        tot = 0;
    }
    void add_edge(int u, int v) {
        edge[tot].v = v;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    int dp[maxn][505];
    LL ans;
    int n, k;
    void dfs(int u, int fa) {
        dp[u][0] = 1;
        for(int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].v;
            if(v == fa) continue;
            dfs(v, u);
            for(int j = 0; j < k; j++) {
                ans += 1LL * dp[v][j] * dp[u][k - j - 1];
            }
            for(int j = 1; j <= k; j++) {
                dp[u][j] += dp[v][j - 1];
            }
        }
    }
    int main() {
    #ifndef ONLINE_JUDGE
        FIN
    #endif
    
        init();
        scanf("%d%d", &n, &k);
        for(int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        dfs(1, 0);
        printf("%lld
    ", ans);
        return 0;
    }
    

    点分治想法

    https://www.cnblogs.com/buerdepepeqi/p/11173338.html

    codeforces 274B

    http://codeforces.com/problemset/problem/274/B

    题意:

    给你一个有n个点的点权树,每次操作可以将点u及其子树的权值全部加一 or 减一,问你最少操作多少次可以将整颗树的权值清零

    题解

    (dp[u][0]表示点u至少减去多少次)

    (dp[u][1]表示点u至少加上多少次)

    (ans=dp[rt][0]+dp[rt][1])

    #include <set>
    #include <map>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> pii;
    typedef unsigned long long uLL;
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    #define bug printf("*********
    ")
    #define FIN freopen("input.txt","r",stdin);
    #define FON freopen("output.txt","w+",stdout);
    #define IO ios::sync_with_stdio(false),cin.tie(0)
    #define debug1(x) cout<<"["<<#x<<" "<<(x)<<"]
    "
    #define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]
    "
    #define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]
    "
    const int maxn = 3e5 + 5;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    const double Pi = acos(-1);
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    LL lcm(LL a, LL b) {
        return a / gcd(a, b) * b;
    }
    double dpow(double a, LL b) {
        double ans = 1.0;
        while(b) {
            if(b % 2)ans = ans * a;
            a = a * a;
            b /= 2;
        } return ans;
    }
    LL quick_pow(LL x, LL y) {
        LL ans = 1;
        while(y) {
            if(y & 1) {
                ans = ans * x % mod;
            } x = x * x % mod;
            y >>= 1;
        } return ans;
    }
    LL dp[maxn][1];
    struct EDGE {
        int v, nxt;
    } edge[maxn << 1];
    int head[maxn], tot;
    void init() {
        memset(head, -1, sizeof(head));
        tot = 0;
    }
    void add_edge(int u, int v) {
        edge[tot].v = v;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    int a[maxn];
    void dfs(int u, int fa) {
        for(int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].v;
            if(v == fa) continue;
            dfs(v, u);
            dp[u][0] = max(dp[u][0], dp[v][0]);
            dp[u][1] = max(dp[u][1], dp[v][1]);
        }
        a[u] = a[u] + dp[u][0] - dp[u][1];
        if(a[u] < 0) {
            dp[u][0] += (-1 * a[u]);
        } else {
            dp[u][1] += a[u];
        }
    }
    int main() {
    #ifndef ONLINE_JUDGE
        FIN
    #endif
        int n;
        init();
        scanf("%d", &n);
        for(int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        dfs(1, 0);
        printf("%lld
    ", dp[1][0] + dp[1][1]);
        return 0;
    }
    

    codeforces 212E

    http://codeforces.com/problemset/problem/212/E

    题意:

    给你一个n个点的树,要求你将其染色,只有两种颜色,使得相邻节点之间不能有不同的颜色

    要求尽可能多的节点染色

    求两种颜色染色个数的所有方案

    题解:

    迷惑题意,根据题意可得,我们一棵树,以某一个点为分割点,分割点不染色,左边染一种颜色,右边染一种颜色,这样就可以满足相邻节点之间没有不同的的颜色,并且染色节点个数为n-1个,已经是最多的了

    我们记录节点子树的大小,以第i个节点为分割点时,得到其子树大小为(sz[i]),然后就是枚举可达性的过程了

    #include <set>
    #include <map>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> pii;
    typedef unsigned long long uLL;
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    #define bug printf("*********
    ")
    #define FIN freopen("input.txt","r",stdin);
    #define FON freopen("output.txt","w+",stdout);
    #define IO ios::sync_with_stdio(false),cin.tie(0)
    #define debug1(x) cout<<"["<<#x<<" "<<(x)<<"]
    "
    #define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]
    "
    #define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]
    "
    const int maxn = 5e3 + 5;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    const double Pi = acos(-1);
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    LL lcm(LL a, LL b) {
        return a / gcd(a, b) * b;
    }
    double dpow(double a, LL b) {
        double ans = 1.0;
        while(b) {
            if(b % 2)ans = ans * a;
            a = a * a;
            b /= 2;
        } return ans;
    }
    LL quick_pow(LL x, LL y) {
        LL ans = 1;
        while(y) {
            if(y & 1) {
                ans = ans * x % mod;
            } x = x * x % mod;
            y >>= 1;
        } return ans;
    }
    struct EDGE {
        int v, nxt;
    } edge[maxn << 1];
    int head[maxn], tot;
    void init() {
        memset(head, -1, sizeof(head));
        tot = 0;
    }
    void add_edge(int u, int v) {
        edge[tot].v = v;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    int n;
    int flag[maxn];
    int sz[maxn];
    int dp[maxn][maxn];
    void dfs(int u, int fa) {
        sz[u] = 1;
        dp[u][0] = 1;
        for(int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].v;
            if(v == fa ) continue;
            dfs(v, u);
            sz[u] += sz[v];
            for(int i = n - 1; i >= 0; i--) {
                if(dp[u][i]) {
                    dp[u][i + sz[v]] = 1;
                }
            }
        }
        int fanum = n - sz[u];
        for(int i = n - 1; i >= 0; i--) {
            if(dp[u][i])
                dp[u][i + fanum] = 1;
        }
    
        for(int i = 1; i < n - 1; i++) {
            if(dp[u][i])
                flag[i] = true;
        }
    }
    int main() {
    #ifndef ONLINE_JUDGE
        FIN
    #endif
        scanf("%d", &n);
        init();
        for(int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        dfs(1, 0);
        int ans = 0;
        for(int i = 1; i < n - 1; i++) {
            if(flag[i]) {
                // printf("%d %d
    ", i, n - i - 1);
                ans++;
            }
        }
        printf("%d
    ", ans);
        for(int i = 1; i < n - 1; i++) {
            if(flag[i]) {
                printf("%d %d
    ", i, n - i - 1);
            }
        }
    
        return 0;
    }
    

    51nod 1405

    https://www.51nod.com/Challenge/Problem.html#problemId=1405

    题意:

    给定一棵无根树,假设它有n个节点,节点编号从1到n, 求1-n这n个节点,到其他n-1个节点的距离之和。

    题解:

    先算出一个点的答案,然后由这个点的答案向其他点更新

    #include <set>
    #include <map>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> pii;
    typedef unsigned long long uLL;
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    #define bug printf("*********
    ")
    #define FIN freopen("input.txt","r",stdin);
    #define FON freopen("output.txt","w+",stdout);
    #define IO ios::sync_with_stdio(false),cin.tie(0)
    #define debug1(x) cout<<"["<<#x<<" "<<(x)<<"]
    "
    #define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]
    "
    #define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]
    "
    const int maxn = 3e5 + 5;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    const double Pi = acos(-1);
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    LL lcm(LL a, LL b) {
        return a / gcd(a, b) * b;
    }
    double dpow(double a, LL b) {
        double ans = 1.0;
        while(b) {
            if(b % 2)ans = ans * a;
            a = a * a;
            b /= 2;
        } return ans;
    }
    LL quick_pow(LL x, LL y) {
        LL ans = 1;
        while(y) {
            if(y & 1) {
                ans = ans * x % mod;
            } x = x * x % mod;
            y >>= 1;
        } return ans;
    }
    struct EDGE {
        int v, nxt;
    } edge[maxn << 1];
    int head[maxn], tot;
    void init() {
        memset(head, -1, sizeof(head));
        tot = 0;
    }
    void add_edge(int u, int v) {
        edge[tot].v = v;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    int sz[maxn];
    LL dp[maxn];
    void dfs1(int u, int fa, int dep) {
        sz[u] = 1;
        dp[1] += dep;
        for(int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].v;
            if(v == fa) continue;
            dfs1(v, u, dep + 1);
            sz[u] += sz[v];
        }
    }
    int n;
    void dfs2(int u, int fa) {
        for(int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].v;
            if(v == fa) continue;
            dp[v] = dp[u] + n - sz[v] * 2;
            dfs2(v, u);
        }
    }
    int main() {
    #ifndef ONLINE_JUDGE
        FIN
    #endif
    
        scanf("%d", &n);
        init();
        for(int i = 1, u, v; i < n; i++) {
            scanf("%d%d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        dfs1(1, 0, 0);
        dfs2(1, 0);
        for(int i = 1; i <= n; i++) {
            printf("%lld
    ", dp[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    模板
    Codeforces Round #598 (Div. 3)
    Codeforces Round #589 (Div. 2)
    日记集
    模板
    Codeforces Round #592 (Div. 2)
    Linux挂载新盘
    VMware实用技巧
    useradd和adduser的区别
    ubuntu VSFTPD搭建FTP服务器 提示530错误
  • 原文地址:https://www.cnblogs.com/buerdepepeqi/p/12719106.html
Copyright © 2011-2022 走看看