树形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("*********\n")
#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)<<"]\n"
#define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]\n"
#define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]\n"
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\n", 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("*********\n")
#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)<<"]\n"
#define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]\n"
#define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]\n"
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\n", 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("*********\n")
#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)<<"]\n"
#define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]\n"
#define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]\n"
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\n", i, n - i - 1);
ans++;
}
}
printf("%d\n", ans);
for(int i = 1; i < n - 1; i++) {
if(flag[i]) {
printf("%d %d\n", 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("*********\n")
#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)<<"]\n"
#define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]\n"
#define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]\n"
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\n", dp[i]);
}
return 0;
}
每一个不曾刷题的日子
都是对生命的辜负
从弱小到强大,需要一段时间的沉淀,就是现在了
~buerdepepeqi