树形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;
}
原文地址:https://www.cnblogs.com/buerdepepeqi/p/12719106.html