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

题目大概说给一棵树,树的边一开始都是损坏的,要修复一些边,修复完后要满足各个点到根的路径上最多只有一条坏的边,现在以各个点为根分别求出修复边的方案数,其结果模1000000007。

不难联想到这题和HDU2196是一种类型的树形DP,因为它们都要分别求各个点的答案。然后解法也不难想:

  • dp0[u]表示只考虑以u结点为根的子树的方案数
  • dp1[u]表示u结点往上走,倒过来,以它父亲为根那部分的方案数

有了这两部分的结果,对于各个点u的答案就是dp0[u]*(dp1[u]+1)。这两部分求法如下,画画图比较好想:

  • 首先求出dp0,这个转移是:dp0[u]=∏(dp0[v]+1)(v是u的孩子),就是对于每个孩子为根的子树的情况总数的乘积,而其中每个孩子的情况总数还要加上一个父亲到孩子之间的边不修复、孩子的子树的边全部修复的情况。
  • 然后求出dp1,转移:求dp1[v],u是v的父亲,dp1[v]=dp0[u]/dp0[v]*(dp1[u]+1)。
  • 现在问题来了,求dp0[u]/dp0[v],注意到结果模1000000007是一个质数,一开始我用乘法逆元WA了,因为虽然1000000007是质数,但1000000007的倍数不与1000000007互质,模1000000007结果是0,这样就出问题了!
  • 本来我想改用线段树做,不过队友提醒说可以分情况讨论,如果不存在与1000000007互质的数直接逆元搞,存在两个以上不与1000000007互质的数那结果就是0,一个的话。。。。。我就不多说了。
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 #define MAXN 222222
 6 struct Edge{
 7     int v,next;
 8 }edge[MAXN<<1];
 9 int NE,head[MAXN];
10 void addEdge(int u,int v){
11     edge[NE].v=v; edge[NE].next=head[u];
12     head[u]=NE++;
13 }
14 long long d[2][MAXN];
15 long long ine(long long a){
16     long long res=1;
17     int n=1000000007-2;
18     while(n){
19         if(n&1){
20             res*=a;
21             res%=1000000007;
22         }
23         a*=a;
24         a%=1000000007;
25         n>>=1;
26     }
27     return res;
28 }
29 void dp0(int u,int fa){
30     long long res=1;
31     for(int i=head[u]; i!=-1; i=edge[i].next){
32         int v=edge[i].v;
33         if(v==fa) continue;
34         dp0(v,u);
35         res*=d[0][v]+1;
36         res%=1000000007;
37     }
38     d[0][u]=res;
39 }
40 void dp1(int u,int fa){
41     int cnt=0;
42     long long tot=1;
43     for(int i=head[u]; i!=-1; i=edge[i].next){
44         int v=edge[i].v;
45         if(v==fa) continue;
46         if((d[0][v]+1)%1000000007==0) ++cnt;
47         else{
48             tot*=d[0][v]+1;
49             tot%=1000000007;
50         }
51     }
52     for(int i=head[u]; i!=-1; i=edge[i].next){
53         int v=edge[i].v;
54         if(v==fa) continue;
55         if(cnt){
56             if((d[0][v]+1)%1000000007==0 && cnt==1){
57                 d[1][v]=tot;
58             }else d[1][v]=0;
59         }else{
60             d[1][v]=d[0][u]*ine((d[0][v]+1)%1000000007);
61             d[1][v]%=1000000007;
62         }
63         d[1][v]*=d[1][u]+1;
64         d[1][v]%=1000000007;
65         dp1(v,u);
66     }
67 }
68 int main(){
69     memset(head,-1,sizeof(head));
70     int n,a;
71     scanf("%d",&n);
72     for(int i=2; i<=n; ++i){
73         scanf("%d",&a);
74         addEdge(a,i);
75         addEdge(i,a);
76     }
77     dp0(1,1);
78     dp1(1,1);
79     for(int i=1; i<=n; ++i){
80         printf("%lld ",d[0][i]*(d[1][i]+1)%1000000007);
81     }
82     return 0;
83 }
时间: 2024-07-30 10:11:50

Codeforces 543D Road Improvement(树形DP+乘法逆元)的相关文章

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子树的时候为了避免除法,对儿子的方案数求一个前缀积和后缀积即可 避免了使用逆元 #incl

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

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表示

51Nod 1677 treecnt 【树形dp+组合数学+逆元】

51Nod 1677  treecnt Description:给定一棵n个节点的树,从1到n标号.选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少. 现需要计算对于所有选择k个点的情况最小选择边数的总和为多少. 样例解释: 一共有三种可能:(下列配图蓝色点表示选择的点,红色边表示最优方案中的边) 选择点{1,2}:至少要选择第一条边使得1和2联通.   选择点{1,3}:至少要选择第二条边使得1和3联通. 选择点{2,3}:两条边都要选择才能使2和3联通. I

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