Codeforces 543D Road Improvement 树形dp

题目链接:点击打开链接

题意:

给定n个点的树

问:

对于以i为根时,把树边黑白染色,使得任意点走到根的路径上不超过一条黑边,输出染色的方案数(mod 1e9+7)

思路:

显然i点的方案数 = i为根的子树的方案数 * 不包括i为根的子树的部分的方案数

首先以i为根的子树求一个方案数down[i]:for all son vj  of i, down[i] = multiply down[j]

求不包括i子树的时候为了避免除法,对儿子的方案数求一个前缀积和后缀积即可

避免了使用逆元

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <vector>
#include <string>
#include <time.h>
#include <math.h>
#include <queue>
#include <stack>
#include <set>
#include <map>
const double eps = 1e-8;
const double pi = acos(-1.0);
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) { putchar('-'); x = -x; }
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
using namespace std;
typedef long long ll;
const int N = 200100;
const int mod = 1e9 + 7;
int n;
vector<int>G[N];
vector<ll>D[N], P[N];
ll down[N], up[N];
ll go[N];
void mul(ll &x, ll y){
	x *= y;
	x %= mod;
}
void dfs_down(int u, int fa){
	down[u] = 1;
	for (int i = 0; i < G[u].size(); i++){
		int v = G[u][i]; if (v == fa){
			D[u].push_back(1); P[u].push_back(1); continue;
		}
		dfs_down(v, u);
		mul(down[u], down[v] + 1);
		D[u].push_back(down[v] + 1);
		P[u].push_back(down[v] + 1);
	}
	for (int i = 1; i < D[u].size(); i++)	mul(D[u][i], D[u][i - 1]);
	for (int i = (int)P[u].size() - 2; i >= 0; i--)
		mul(P[u][i], P[u][i + 1]);
}
void dfs_up(int u, int fa, ll way){
	up[u] = way;
	for (int i = 0; i < G[u].size(); i++){
		int v = G[u][i]; if (v == fa)continue;
		ll tmp = way;
		if (i && i < (int)D[u].size()) mul(tmp, D[u][i - 1]);
		if (i + 1 < P[u].size())
			mul(tmp, P[u][i + 1]);
		dfs_up(v, u, tmp + 1);
	}
}
int main(){
	rd(n);
	for (int i = 2, x; i <= n; i++){
		rd(x); G[x].push_back(i); G[i].push_back(x);
	}
	dfs_down(1, 1);
	dfs_up(1, 1, 1);
	for (int i = 1; i <= n; i++)pt(down[i] * up[i] % mod), putchar(' ');
	return 0;
}
时间: 2024-10-27 13:46:19

Codeforces 543D Road Improvement 树形dp的相关文章

Codeforces 543D Road Improvement(DP)

题目链接 Solution 比较明显的树形DP模型. 首先可以先用一次DFS求出以1为根时,sum[i](以i为子树的根时,满足要求的子树的个数). 考虑将根从i变换到它的儿子j时,sum[i]产生的变化. 在变化前sum[i]不为0时,可以用求逆元的方法求出新的sum[i]. sum[i]为0时,就需要遍历i的新的儿子. 官方的题解给出了一个比较好的做法是预处理i的儿子的前缀积,和后缀积.使用的时候只要去除相应的儿子. #include <bits/stdc++.h> #define LL

Codeforces 543D Road Improvement(树形DP+乘法逆元)

题目大概说给一棵树,树的边一开始都是损坏的,要修复一些边,修复完后要满足各个点到根的路径上最多只有一条坏的边,现在以各个点为根分别求出修复边的方案数,其结果模1000000007. 不难联想到这题和HDU2196是一种类型的树形DP,因为它们都要分别求各个点的答案.然后解法也不难想: dp0[u]表示只考虑以u结点为根的子树的方案数 dp1[u]表示u结点往上走,倒过来,以它父亲为根那部分的方案数 有了这两部分的结果,对于各个点u的答案就是dp0[u]*(dp1[u]+1).这两部分求法如下,画

CodeForces 543d Road Improvement

Road Improvement Time Limit: 2000ms Memory Limit: 262144KB This problem will be judged on CodeForces. Original ID: 543D64-bit integer IO format: %I64d      Java class name: (Any) The country has n cities and n - 1 bidirectional roads, it is possible

Codeforces 123E Maze(树形DP+期望)

[题目链接] http://codeforces.com/problemset/problem/123/E [题目大意] 给出一棵,给出从每个点出发的概率和以每个点为终点的概率,求出每次按照dfs序从起点到达终点的期望. [题解] 首先对于期望计算有X(x,y)=X(x)*X(y),所以对于每次dfs寻路只要求出其起点到终点的期望步数,乘上起点的概率和终点的概率即可.对于一个固定起点和终点的dfs寻路,我们可以发现如果一个点在必要路径上,那么这条路被走过的期望一定为1,如果不在必要路线上,那么走

bzoj 4424: Cf19E Fairy &amp;&amp; codeforces 19E. Fairy【树形dp】

参考:https://blog.csdn.net/heheda_is_an_oier/article/details/51131641 这个找奇偶环的dp1真是巧妙,感觉像tarjan一样 首先分情况讨论,如果没有奇环,每条边都可以删:如果有一个奇环,奇环上隋边山:否则,删被所有奇环覆盖且没被任何一个偶环覆盖的边 那么重点就是怎样找到所有的奇环和偶环 用树形dp来搞,设f[i]记录经过第i条边的奇环数,g[i]记录经过第i条边的偶环数,因为是边的编号而存的是双向边,所以dp的时候用i>>1表示

codeforces 455C C. Civilization(树形dp+树的直径+并查集)

题目链接: codeforces 455C 题目大意: 给出一些点,他们之间初始存在一些边,给出两种操作,第一种是查询某个点所在的树的直径,另一种是将两个树合并,要求使合并后的树的直径最小. 题目分析: 首先算取没做操作前的连通块里的树的直径,也就是先dfs一遍,找到深度最大的点,然后从这个点再搜,找到的最远的距离就是这棵树的直径,因为可以证明从根搜深度最大的点一定是树的直径的一个端点,因为它可以通过到达次大的深度的点或者找到与它公共祖先不在根处的获得树的直径. 然后每次合并,我们可以知道得到的

Codeforces 212E IT Restaurants 树形dp(水

题目链接:点击打开链接 题意: 给定n个点的树,染两种颜色,不同颜色不能相邻且要给尽可能多的节点染色.求颜色A和颜色B可能的染色节点个数.(copy from figo) 而且至少一个点染A,至少一个点染B dp[i][j]=1表示i点染j个A色是可行的 =0表示不可行. import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.math.Big

Educational Codeforces Round 52F(树形DP,vector)

#include<bits/stdc++.h>using namespace std;int n,k;vector<int>son[1000007];int dp[1000007],depth[1000007],ans[1000007];//dp[i]表示离i最近的叶子节点距离i的深度,depth[i]表示以i为根,回到i所能到达的叶子节点的数量,ans[i]表示以i为根,能到达的叶子节点数目最大,即题意所需void dfs(int now){    if(!son[now].si

CodeForces 158E Phone Talks 树形dp+计数

题目链接:点击打开链接 题意:给定n个点的树,常数d 给出每个点的权值,问有多少种划分方法使得划分后每个连通块里的最大权值-最小权值<=d 思路:点击打开链接 枚举每个点i 使得i是集合中的最小值. 则枚举时已经使得i是最小值,然后这个问题就变成单纯的划分问题了,上面链接里的题解已经很详尽了 import java.io.PrintWriter; import java.text.DecimalFormat; import java.util.ArrayDeque; import java.ut