codeforces 219D 树形dp

题目大意:

根据n个城市,设置n-1条有向边,希望设定一个中心城市,能通过修改一些道路的方向到达任何一座城市,希望修改的道路数量最少

输出这个最少的道路数量,并且把所有符合的可作为中心的城市编号一个个输出来

开始也实在想不出,根据树形dp,首先要确定一棵树,可是这里的边乱七八糟的,没法确定根

后来看了别人的思路,原来还是自己太死脑筋了,根本不需要确定根,可以把任意一个城市作为根来解决题目,这里假定1为根

把所有的有向边看作无向边,但要记录那条边是真实存在的,哪条是自己加的

用dp[i]表示 i 点到达其对应的子树中的所有点需要修改的边的数量

用一次dfs得到dp[i]

然后网上的大牛们都说2次dfs,后一次因为没写清楚,自己也不想看代码,就自己想了,可就是没办法写出dfs,最后发现直接就可以循环做。。。而且更好理解

做法:

len[i]记录 i 到 root 这里定义root为 1 长度,也就是i 到 1 之间有多少条边

change[i] 表示这 len[i]条边中有多少条改变了

这两个值可以在第一次dfs中实现

然后对于每一个点 i 来说,其实为了让 i 能够到达任何一点,只要改动 i 到 1 这len[i]条边即可,画个图很容易发现

1作为树根,i 到 1 的过程经过的都是一棵棵子树的根,能到达这些子树的根,必能到达子树中的任何一点,所以希望能够到达 1 即可

用rec[i]记录以 i 为中心城市需要改动的边

rec[i] = dp[1] - change[i] + (len[i] - change[i]) //减第一个change[i]是把原来改过的边改回来,然后+后面的是因为为了 i 到1,需要将那些

方向正常的 len[i] - change[i]倒转

最后通过rec[i]得到答案即可= =

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 const int N = 200005;
 6
 7 int first[N] , k , len[N] , change[N] , dp[N] , rec[N];
 8
 9 struct Edge{
10     int y , next;
11     bool flag;
12 }e[N<<1];
13
14 void add_edge(int x , int y , bool flag)
15 {
16     e[k].y = y , e[k].next = first[x] , e[k].flag = flag;
17     first[x] = k++;
18 }
19
20 void dfs(int u , int fa)
21 {
22     for(int i = first[u] ; i!=-1 ; i=e[i].next){
23         int v = e[i].y;
24         if(v == fa) continue;
25         len[v] = len[u]+1;
26         change[v] = change[u];
27         if(!e[i].flag) change[v]++;
28         dfs(v , u);
29         if(!e[i].flag){
30             dp[u] += dp[v]+1;
31         }
32         else{
33             dp[u] += dp[v];
34         }
35     }
36 }
37
38 int main()
39 {
40    // freopen("a.in" , "r" , stdin);
41     int n , a , b;
42     while(scanf("%d" , &n) == 1)
43     {
44         memset(first , -1 , sizeof(first));
45         k = 0;
46         for(int i=1 ; i<n ; i++){
47             scanf("%d%d" , &a , &b);
48             add_edge(a , b , true);
49             add_edge(b , a , false);
50         }
51         memset(dp , 0 , sizeof(dp));
52         len[1] = 0 , change[1] = 0;
53         dfs(1 , -1);
54
55         int minn = 200000000;
56
57         for(int i=1 ; i<=n ; i++){
58             rec[i] = dp[1] + len[i] - 2*change[i];
59            // cout<<"i: "<<i<<" "<<len[i]<<" "<<change[i]<<" "<<rec[i]<<endl;
60             minn = min(minn , rec[i]);
61         }
62         printf("%d\n" , minn);
63         int num = 0;
64         for(int i=1 ; i<=n ; i++){
65             if(minn == rec[i]){
66                 if(num == 0) printf("%d" , i);
67                 else printf(" %d" , i);
68                 num ++;
69             }
70         }
71         printf("\n");
72     }
73     return 0;
74 }
时间: 2024-10-19 10:47:17

codeforces 219D 树形dp的相关文章

Codeforces 77C 树形dp + 贪心

题目链接:点击打开链接 题意: 给定n个点, 每个点的豆子数量 下面是一棵树 再给出起点 每走到一个点,就会把那个点的豆子吃掉一颗. 问:回到起点最多能吃掉多少颗豆子 思路:树形dp 对于当前节点u,先把子节点v都走一次. 然后再往返于(u,v) 之间,直到u点没有豆子或者v点没有豆子. dp[u] 表示u点的最大值.a[u] 是u点剩下的豆子数. #include <cstdio> #include <vector> #include <algorithm> #inc

CodeForces 274B 树形dp

//继续水一道树形dp 1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "algorithm" 5 #include "cmath" 6 using namespace std; 7 __int64 dp[100010][2]; 8 bool vis[100010]; 9 int tot, first[100

Codeforces 1088E 树形dp+思维

比赛的时候看到题意没多想就放弃了.结果最后D也没做出来,还掉分了,所以还是题目做的太少,人太菜. 回到正题: 题意:一棵树,点带权值,然后求k个子连通块,使得k个连通块内所有的点权值相加作为分子除以k的值最大,如果这样的最大值有多个,就最大化k. 赛后看了看别人的代码仔细想了一想,还是挺容易的. 首先将树分为若干个连通块,考虑一个权值求和最大的连通块,设该最大值为sum,那么其他所有的连通块,权值求和都不可能比当前的连通块大. 在不考虑最大化k的情况下,就没有必要再将其他的连通块加进来(因为如果

Codeforces 743D 树形dp

D. Chloe and pleasant prizes 题意:一棵树,以结点1为根结点悬挂在墙上,每个点有一个权值.选两条边切断,要求:刚好掉下两份结点,且两份不能属于同一子树.求两份结点可能的最大权值和. tags:裸的树dp. #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for

codeforces 500d 树形dp和概率

求期望,公式是从树中任选3个点,共有C(n, 3)种情况,所有情况的和 / C(n, 3)即为答案. 对于树中的任意两个点,可以构成一条固定的路径,再从剩下的n - 2个点找一个点,可以得到另外两条路径,共有n - 2种情况. 所以每条路径出现的次数为n - 2次. 假设边Ei共有Ci条路径经过,则对于i = 1 : n, Wi * Ci的和即为所有路径的权和, 结果为所有路径的权和 * (n - 2) / C(n, 3); 化简得到公式:所有路径的权和 / n / (n - 1) * 6. #

CodeForces 109C 树形DP Lucky Tree

赶脚官方题解写得挺清楚的说,=_= 注意数据范围用long long,否则会溢出. 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <vector> 6 using namespace std; 7 8 const int maxn = 100000 + 10; 9 10 int n; 11 vecto

CodeForces 519E 树形DP A and B and Lecture Rooms

给出一棵树,有若干次询问,每次询问距两个点u, v距离相等的点的个数. 情况还挺多的,少侠不妨去看官方题解.^_^ 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 using namespace std; 7 8 const int maxn = 100000 + 10; 9 10

Codeforces 1065F(树形dp)

题目链接 题意:给一棵树,进行如下操作,如果当前点非叶子,则往子树移动,否则最多向上移动k次,问从根节点开始最多访问多少叶子 思路:预处理出每个点最多能“白嫖”到几个叶子,根据下一个点的状态更新最优方案 #include <bits/stdc++.h> #define DBG(x) cerr << #x << " = " << x << endl; const int maxn = 1e6+5; const int maxm

CodeForces 219D.Choosing Capital for Treeland (树形dp)

题目链接: http://codeforces.com/contest/219/problem/D 题意: 给一个n节点的有向无环图,要找一个这样的点:该点到其它n-1要逆转的道路最少,(边<u,v>,如果v要到u去,则要逆转该边方向)如果有多个这样的点,则升序输出所有 思路: 看了三篇博客,挺好的 http://blog.csdn.net/chl_3205/article/details/9284747 http://m.blog.csdn.net/qq_32570675/article/d