树上的DP

CF#196B http://codeforces.com/contest/338/problem/B

题意:在一颗树上,给m个点,求到所有m个点距离不超过d的点的个数,所有路径长度为1。

分析:问题转化,就是求一个点是否到离它最远的给定点的距离小于d,

先第一遍dfs1求出以当前节点u为根的子树到子树中离它最远的给定点的距离d1[u],和次远的距离d2[u],

并且记录下最远距离是从哪个son转移过来的,fo[u];

第二遍dfs2求出当前节点从它fa出发能碰到给定点的最远距离up[u],那么对于当前点u,max(d1[u],up[u]),就是u到

所有给定点的最远的距离;

up[]的可以按照dfs的顺序递推求出,假设我们已经求出up[c1],现在求up[c2]

up[c2]表示从c2的fa结点也就是c1那边给定结点(红色圆点)到c2的最远距离

那么up[c2] 由两部分转移过来,一个是原先以c1为根的子树除去c2的分支后的最远距离,这就是为什么要记录最远是从哪个son转移过来

dd = (fo[c1] == c2 ? d2[c1] : d1[c1]);

还有就是up[c1];显然up[c2] = max(dd,up[c1])+1;(up[c1]表示从c6那边转移过来的值)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<vector>
 7 #include<cmath>
 8 #define pbk push_back
 9 using namespace std;
10 const int N = 100000+10;
11 const int INF = 1000000+10;
12 vector<int> g[N];
13 int n,m,d;
14 bool p[N];
15 int d1[N],d2[N],fo[N],up[N];
16 int co[N];
17 void update(int c,int rt,int x,int &m1,int &m2) {
18     if (x >= m1) {
19         m2 = m1; m1 = x;
20         fo[rt] = c;
21     }else if (x < m1) {
22         if (x > m2) m2 = x;
23     }
24 }
25 void dfs1(int rt,int fa) {
26     int sz = g[rt].size();
27     d1[rt] = d2[rt] = -INF;
28     for (int i = 0; i < sz; i++) {
29         int c = g[rt][i];
30         if (c == fa) continue;
31         dfs1(c,rt);
32         if (co[c]) {
33             update(c,rt,d1[c]+1,d1[rt],d2[rt]);
34             co[rt] = 1;
35         }
36     }
37     if (p[rt]) {
38         if (d1[rt] > 0 && d2[rt] < 0) d2[rt] = 0;
39         if (d1[rt] < 0) {
40             d1[rt] = 0; fo[rt] = rt;
41         }
42         co[rt] = 1;
43     }
44 }
45 void dfs2(int rt,int fa) {
46     int sz = g[rt].size();
47     for (int i = 0; i < sz; i++) {
48         int c = g[rt][i];
49         if (c == fa) continue;
50         int dd = (fo[rt] == c ? d2[rt] : d1[rt]);
51         up[c] = max(dd, up[rt]) + 1;
52         dfs2(c,rt);
53     }
54 }
55 void solve(){
56     memset(co,0,sizeof(co));
57     memset(fo,-1,sizeof(fo));
58     dfs1(1,-1);
59     up[1] = -INF;
60     dfs2(1,-1);
61   
62     int ans = 0;
63     for (int i = 1; i <= n; i++) {
64         if (d1[i] <= d && up[i] <= d) ans++;
65     }
66     printf("%d\n",ans);
67 }
68 int main(){
69     while (~scanf("%d%d%d",&n,&m,&d)) {
70         memset(p,0,sizeof(p));
71         for (int i = 0; i < m; i++) {
72             int f; scanf("%d",&f);
73             p[f] = 1;
74         }
75         for (int i = 0; i <= n; i++) g[i].clear();
76         for (int i = 0; i < n-1; i++) {
77             int u,v; scanf("%d%d",&u,&v);
78             g[u].pbk(v); g[v].pbk(u);
79         }
80         solve();
81     }
82     return 0;
83 }
View Code

 

hdu4679 http://acm.hdu.edu.cn/showproblem.php?pid=4679

题意:给n个结点的树,每条边上都有一个w,去掉一条边后变成两颗树,选一条边使得w*max(diaA,diaB);其中diaA,diaB表示树的直径(树上最长路);

分析:删掉一条边后,分成两部分,一部分是原先的子树,对于所有子树我们可以一遍dfs求出它的dia,问题我们该怎么求出

另外一部分的dia,设dia[u]表示从u的fa那一边能得到的最大直径,dis[u]表示u的fa那边能得到的最大距离,

那么这一部分的dia就是max(dia[u],dis[u]+1+md, dd);其中md表示删掉边后以u为根的子树叶子节点到u的最远距离,

dd表示删掉边后以u为根的子树的直径;这样就可以求出删掉一条边后的解了;

dd是B部分,dis[u]+1+md是A,B和起来的部分,dia[u]表示A部分;

接着就是该如何求dia[u], dis[u];

dis[u]的求法和上一题一样,dia[u]的求法也差不多

在第一遍dfs的时候保留下以u为根的子树到u的最大,次大,次次大的距离,并且保存下最大,次大是从哪个son转移过来的,

这样就可以再第二遍dfs的时候递推求出dia[u],dis[u];

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<vector>
 8 #include<set>
 9 #pragma comment(linker, "/STACK:1024000000,1024000000")
10 #define pbk push_back
11 using namespace std;
12 const int N = 100000+10;
13 const int INF = 1000000+10;
14 struct edge{
15     int v,w,id;
16     edge(int v,int w,int id):v(v),w(w),id(id){}
17 };
18 vector<edge> g[N];
19 int n;
20 int md1[N],md2[N],md3[N],fro1[N],fro2[N];
21 void update(int x,int c,int rt) {
22     if (x >= md1[rt]) {
23         md3[rt] = md2[rt];
24         md2[rt] = md1[rt]; fro2[rt] = fro1[rt];
25         md1[rt] = x; fro1[rt] = c;
26     }else if (x >= md2[rt]){
27         md3[rt] = md2[rt];
28         md2[rt] = x; fro2[rt] = c;
29     }else if (x > md3[rt]) md3[rt] = x;
30 }
31 void dfs1(int rt,int fa) {
32     int sz = g[rt].size();
33     md1[rt] = md2[rt] = md3[rt] = 0;
34     for (int i = 0; i < sz; i++) {
35         edge c = g[rt][i];
36         if (c.v == fa) continue;
37         dfs1(c.v,rt);
38         update(md1[c.v]+1,c.v,rt);
39     }
40 }
41 int dis[N],dia[N];
42 int an1,an2;
43 void dfs2(int rt,int fa) {
44     int sz = g[rt].size();
45     for (int i = 0; i < sz; i++) {
46         edge c = g[rt][i];
47         if (c.v == fa) continue;
48         int dd ,md;
49         if (fro1[rt] == c.v) {
50             dd = md2[rt] + md3[rt]; md = md2[rt];
51         }else if (fro2[rt] == c.v) {
52             dd = md1[rt] + md3[rt]; md = md1[rt];
53         } else {
54             dd = md1[rt] + md2[rt]; md = md1[rt];
55         }
56         dd = max(dd, max(dia[rt],dis[rt] + 1 + md));
57         dia[c.v] = dd;
58         dd = max(dd, md1[c.v] + md2[c.v]);
59         dis[c.v] = max(md, dis[rt] + 1);
60         if (an1 == -1) {
61             an1 = c.id; an2 = dd * c.w;
62         }else if (dd * c.w < an2) {
63             an1 = c.id; an2 = dd * c.w;
64         }else if (dd * c.w == an2 && c.id < an1) {
65             an1 = c.id;
66         }
67 
68         dfs2(c.v,rt);
69     }
70 }
71 void solve(){
72     dfs1(1,-1);
73     dia[1] = -INF; dis[1] = -INF;
74     an1 = -1;
75     dfs2(1,-1);
76     printf("%d\n",an1);
77 }
78 int main(){
79     int T,cas = 0; scanf("%d",&T);
80     while (T--) {
81         scanf("%d",&n);
82         for (int i = 0; i <= n; i++) g[i].clear();
83         for (int i = 1; i < n; i++) {
84             int u,v,w; scanf("%d%d%d",&u,&v,&w);
85             g[u].pbk(edge(v,w,i));
86             g[v].pbk(edge(u,w,i));
87         }
88         printf("Case #%d: ",++cas);
89         solve();
90     }
91     return 0;
92 }
View Code

 

 hdu 4661 http://acm.hdu.edu.cn/showproblem.php?pid=4661

题意:n个,他们之间的关系是一棵树,每个人都有一个消息,每次可以选一个人把他知道的所有消息传给另一个人,问

所有人知道所有消息的方案数,假设要传k次,其中只要有一次传出的人或接受的人不同都算不同;

分析:每个人至少要把自己的消息传出去,至少要接受一次消息,很明显,确定树根后只要从叶子节点一层一层传上来

直到跟然后再从跟传下去,一共(n-1)*2,是最优的;求方案数,首先是从叶子节点传到根,其实就是所有节点的拓扑序的个数;

设f[u]表以u为根节点的子树的拓扑序的总数num[u]表示子树的结点数,

那么f[u] = f[son1] * f[son2] * f[soni] * (num[u]-1)!/(num[son1]! * num[son2]! * num[soni] ! );

通过一遍dfs在确定根后,可以求出所有子树的f[],在第二遍dfs的时候通过维护从当前结点的fa那边的拓扑序列,和num[]值

递推求出以当前结点为树根的最终方案; 

然后是根传到叶子结点是等价于从叶子结点传到根的,所有累加所有方案数的平方就是最终解;

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<vector>
 8 #pragma comment(linker, "/STACK:1024000000,1024000000") 
 9 #include<set>
10 using namespace std;
11 typedef long long LL;
12 const int N = 1000000+10;
13 const int Mod = 1e9+7;
14 vector<int> g[N];
15 int n;
16 LL ifac[N],fac[N];
17 void ex_gcd(LL a, LL b, LL &g, LL &x, LL &y) {
18     if (b == 0) {
19         g = a; x = 1; y = 0;
20     }else {
21         ex_gcd(b, a%b, g, y, x);
22         y -= x * (a/b);
23     }
24 }
25 LL inv(LL a, LL n) {
26     LL x,y,g;
27     ex_gcd(a,n,g,x,y);
28     return g == 1 ? (x % n + n) % n : -1;
29 }
30 void init(){
31     fac[0] = 1; ifac[0] = 1;
32     for (int i = 1; i < N; i++) {
33         fac[i] = fac[i-1] * i % Mod;
34         ifac[i] = inv(fac[i],Mod);
35     }
36 }
37 int num[N],f[N];
38 void dfs1(int rt,int fa) {
39     int sz = g[rt].size();
40     num[rt] = 0; f[rt] = 1;
41     for (int i = 0; i < sz; i++) {
42         int c = g[rt][i];
43         if (c == fa) continue;
44         dfs1(c,rt);
45         num[rt] += num[c];
46         f[rt] = (LL)f[rt] * f[c] % Mod * ifac[num[c]] % Mod;
47     }
48     num[rt] += 1;
49     f[rt] = (LL)f[rt] * fac[num[rt] - 1] % Mod;
50 }
51 LL ans;
52 int up[N],cn[N];
53 void dfs2(int rt,int fa) {
54     int sz = g[rt].size();
55     for (int i = 0; i < sz; i++) {
56         int c = g[rt][i];
57         if (c == fa) continue;
58 
59         LL tmp = (LL)f[rt] * ifac[num[rt] - 1] % Mod * fac[num[c]] % Mod * inv(f[c],Mod) % Mod ;
60         tmp = tmp * up[rt] % Mod * ifac[n - num[rt]] % Mod * fac[n - num[c] - 1] % Mod;
61         up[c] = tmp;
62 
63         LL tp = f[c] * ifac[num[c] - 1] % Mod  * tmp % Mod * ifac[n - num[c]] % Mod * fac[n-1] % Mod;
64         ans = (ans +  tp * tp) % Mod;
65         dfs2(c,rt);
66     }
67 }
68 void solve(){
69     dfs1(1,-1);
70     ans = (LL)f[1] * f[1] % Mod;
71     up[1] = 1;
72     dfs2(1,-1);
73     printf("%I64d\n",ans);
74 }
75 int main(){
76     init();
77     int T; scanf("%d",&T);
78     while (T--) {
79         scanf("%d",&n);
80         for (int i = 0; i <= n; i++) g[i].clear();
81         for (int i = 0; i < n-1; i++) {
82             int u,v; scanf("%d%d",&u,&v);
83             g[u].push_back(v); g[v].push_back(u);
84         }
85         solve();
86     }
87     return 0;
88 }
View Code

 

hdu 4276 http://acm.hdu.edu.cn/showproblem.php?pid=4276

题意:给你一棵树,路径上有一个花费,每个节点有一个价值,问在T时间内,从1走到n能得到的最大价值;

分析:首先因为在树上,所有从1到n的路径是唯一的,也就是唯一路径上的边我们只会走一次,然后我们可以想象

如果把1和n结点拉直后,会变成一个森林并且树的根都是在路径上的,并且如果要去拿那里的价值每条路径都要经历2边;

思路就很明了,先预处理出所有路径上的点,然后以路径上的结点为子树根求出以每个根花费j能得到的最大代价,

最后就是分组背包,每组选一个;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<cstring>
 7 #include<vector>
 8 #define mp make_pair
 9 #define pbk push_back
10 using namespace std;
11 const int N = 100+10;
12 typedef pair<int,int> pii;
13 int dp[N][500+10];
14 int n,T;
15 vector<pii> G[N];
16 int val[N];
17 int On[N], f, sti;
18 void dfs_find(int u,int fa,int dep) {
19     int sz = G[u].size();
20     On[u] = 1;
21     if (u == n) {
22         f = 1;
23         return;
24     }
25     for (int i = 0; i < sz; i++) {
26         int v = G[u][i].first;
27         if (v == fa) continue;
28         sti = sti + G[u][i].second;
29         dfs_find(v,u,dep+1);
30         if (f) return;
31         sti = sti - G[u][i].second;
32     }
33     On[u] = 0;
34 }
35 void dfs(int u,int fa) {
36     int sz = G[u].size();
37     for (int i = 0; i <= T - sti; i++) dp[u][i] = val[u];
38     for (int i = 0; i < sz; i++) {
39         int v = G[u][i].first;
40         if (On[v] || v == fa) continue;
41         dfs(v, u);
42         int ti = G[u][i].second * 2;
43         for (int j = T - sti; j >= 0; j--) {
44             for (int k = 0; k <= j - ti; k++) {
45                 dp[u][j] = max(dp[u][j], dp[u][j - k - ti] + dp[v][k]);
46             }
47         }
48     }
49 
50 }
51 int an[500+10];
52 void solve(){
53     memset(On,0,sizeof(On));
54     f = 0; sti = 0;
55     dfs_find(1,-1,0);
56     if (sti > T) {
57         puts("Human beings die in pursuit of wealth, and birds die in pursuit of food!");
58         return;
59     }
60     memset(dp,0,sizeof(dp));
61     for (int i = 1; i <= n; i++) {
62         if (On[i]) {
63           dfs(i,-1);
64         }
65     }
66     //分组背包 
67     memset(an,0,sizeof(an));
68     for (int i = 1; i <= n; i++) if (On[i]){
69         for (int j = T - sti; j >= 0; j--) {
70             for (int k = 0; k <= j; k++) {
71                 an[j] = max(an[j], an[j - k] + dp[i][k]);
72             }
73         }
74     }
75     int idx = 0;
76     for (int i = 0; i <= T - sti; i++) {
77         if (an[i] > an[idx]) idx = i;
78     }
79     printf("%d\n",an[idx]);
80 
81 }
82 int main(){
83     while (~scanf("%d%d",&n,&T)) {
84         for (int i = 0; i <= n; i++) G[i].clear();
85         for (int i = 0; i < n-1; i++) {
86             int u,v,w; scanf("%d%d%d",&u,&v,&w);
87             G[u].pbk(mp(v,w)); G[v].pbk(mp(u,w));
88         }
89         for (int i = 1; i <= n; i++) scanf("%d",val+i);
90         solve();
91     }
92     return 0;
93 }
View Code

 

 

 hdu 4044

题意:可以在结点上建至多一个炮台,花费已知,攻击力已知,求只有m费用的情况下从结点1到叶子结点的路径上攻击力之和的最小值最大;

分析:对于结点1,首先我们要分配m使得它的各个son的攻击力之和最小值经历大,dp[u][m]表示的就是以结点u为子树根花费m能得到的对于该子树的最小值最大;

现在的问题就是这么推出dp[u][m]; 显然 对已son1来说dp[u][m] = dp[son1][m]; 对于son2: tp = max(tp,min(dp[u][m-k], dp[son2][k])); dp[u][m] = tp;此时的dp[u][m-k]表示的前i个son都取完之后最小的攻击力最大的值;  当遍历完son之后,那么再加上u位置能建立的炮台,就是最终的答案;

trick : 因为花费有为zero的,所有直接dp[u][m] = max(dp[u][m], dp[u][m-x] + y);是有问题的,当存在两次以上花费为0的数据就会错误,所以要预先处理处理花费为i最大攻击力;

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<cstdlib>
  7 #include<vector>
  8 #include<map>
  9 #define pbk push_back
 10 #define mp make_pair
 11 using namespace std;
 12 const int N = 1000+10;
 13 typedef pair<int,int> pii;
 14 int dp[N][200+10];
 15 int n,m;
 16 vector<int> g[N];
 17 vector<pii> tow[N];
 18 int tp[N][200+10];
 19 void dfs(int u,int fa) {
 20     bool first = true;
 21     for (int i = 0; i < g[u].size(); i++) {
 22         int  v = g[u][i];
 23         if (v == fa) continue;
 24         dfs(v,u);
 25         if (first) {
 26             for (int j = 0; j <= m; j++) dp[u][j] = dp[v][j];
 27             first = false;
 28             continue;
 29         }
 30         for (int j = m; j >= 0; j--) {
 31             int tp = 0;
 32             for (int k = 0; k <= j; k++) {
 33                 int tmp = min (dp[u][j-k], dp[v][k]);
 34                 //if (first) tmp = dp[v][k];
 35                 tp = max(tp, tmp);
 36             }
 37             dp[u][j] = tp;
 38         }
 39         //first = false;
 40     }
 41 
 42     for (int i = m; i >= 0; i--) {
 43        /* for (int j = 0; j < tow[u].size(); j++) {
 44              int x = tow[u][j].first, y = tow[u][j].second;
 45              dp[u][i] = max(dp[u][i], dp[u][i-x] + y);
 46         }*/
 47         for (int j = 0; j <= i; j++) dp[u][i] = max(dp[u][i], dp[u][i - j] + tp[u][j]);
 48     }
 49 }
 50 void solve() {
 51     for (int i = 1; i <= n; i++) {
 52         for (int j = 1; j <= m; j++) tp[i][j] = max(tp[i][j], tp[i][j-1]);
 53     }
 54     memset(dp, 0, sizeof(dp));
 55     dfs(1,-1);
 56     int idx = 0;
 57     for (int i = 0; i <= m; i++) {
 58         if (dp[1][i] > dp[1][idx]) idx = i;
 59     }
 60     printf("%d\n",dp[1][idx]);
 61 }
 62 int main(){
 63     int T; scanf("%d",&T);
 64     while (T--) {
 65         scanf("%d",&n);
 66         for (int i = 0; i <= n; i++) g[i].clear();
 67 
 68         for (int i = 0; i < n-1; i++) {
 69             int u,v; scanf("%d%d",&u,&v);
 70             g[u].pbk(v); g[v].pbk(u);
 71         }
 72         scanf("%d",&m);
 73         memset(tp,0,sizeof(tp));
 74         for (int i = 1; i <= n; i++) {
 75             int c; scanf("%d",&c);
 76             tow[i].clear();
 77             for (int j = 0; j < c; j++) {
 78                 int u,v; scanf("%d%d",&u,&v);
 79          //       tow[i].pbk(mp(u,v));
 80                 tp[i][u] = max(tp[i][u],v);
 81             }
 82         }
 83 
 84         solve();
 85 
 86     }
 87 
 88     return 0;
 89 }
 90 
 91 /*
 92 2
 93 2
 94 1 2
 95 30
 96 3 10 20 20 40 30 50
 97 3 10 30 20 40 30 45
 98 4
 99 2 1
100 3 1
101 1 4
102 9
103 3 10 20 20 40 30 50
104 3 10 30 20 40 30 45
105 3 10 30 20 40 30 35
106 3 10 30 20 40 30 35
107 
108 */
View Code

 

 CF 219D

题意:一棵树,路径是由方向的,选一个节点,使得旋转最少的边使得从该结点到所有结点都有路;

分析:如果路径分别标记为1,-1那么就是统计以某个结点为根,-1的最小个数,dp[u]表示以u为根的子树下有多少个-1;

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<set>
 9 #define pbk push_back
10 #define mp make_pair
11 using namespace std;
12 const int N = 200000 + 10;
13 typedef pair<int,int> pii;
14 vector<pii> g[N];
15 int n;
16 int dp[N];
17 vector<int> an;
18 int up;
19 void dfs1(int u,int fa) {
20     int sz = g[u].size();
21     dp[u] = 0;
22     for (int i = 0; i < sz; i++) {
23         int v = g[u][i].first;
24         if (v == fa) continue;
25         dfs1(v,u);
26         dp[u] += dp[v] + (g[u][i].second == -1 ? 1 : 0);
27     }
28 }
29 void update(int u,int v) {
30     if (u < up) {
31         up = u;
32         an.clear(); an.pbk(v);
33     }else if ( u == up ) an.pbk(v);
34 }
35 void dfs2(int u,int fa,int ba) {
36     int sz = g[u].size();
37 
38     for (int i = 0; i < sz; i++) {
39         int v = g[u][i].first;
40         if (v == fa) continue;
41         int tp = dp[u] - dp[v] - (g[u][i].second == -1 ? 1 : 0) + ba + (g[u][i].second == 1 ? 1 : 0);
42         update(dp[v] + tp,v);
43         dfs2(v,u,tp);
44     }
45 }
46 void solve(){
47     memset(dp,0,sizeof(dp));
48     dfs1(1,-1);
49     up = dp[1];
50     an.clear();
51     an.pbk(1);
52     dfs2(1,-1,0);
53     printf("%d\n",up);
54     sort(an.begin(),an.end());
55     for (int i = 0; i < an.size(); i++) {
56         printf("%d%c",an[i],i == an.size() - 1 ? '\n' : ' ');
57     }
58 }
59 int main() {
60     while (~scanf("%d",&n)) {
61         for (int i = 0; i <= n; i++) g[i].clear();
62         for (int i = 0; i < n-1; i++) {
63             int u,v;  scanf("%d%d",&u,&v);
64             g[u].pbk(mp(v,1)); g[v].pbk(mp(u,-1));
65         }
66         solve();
67     }
68     return 0;
69 }
View Code

 

CF 120F http://codeforces.com/problemset/problem/120/F

题意:给你n棵树,累加每棵树的直径;

分析:直接求;该题CF上是文件读入的;

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<vector>
 8 #define pbk push_back
 9 #define mp make_pair
10 using namespace std;
11 typedef pair<int,int> pii;
12 const int N = 100+10;
13 vector<int> g[N];
14 int n;
15 int an;
16 int dia[N],di1[N],di2[N],fo[N],mxdia;
17 void update(int x,int u,int v) {
18     if (x > di1[u]) {
19         di2[u] = di1[u];
20         di1[u] = x;
21         fo[u] = v;
22     }else if (x == di1[u] && x > di2[u]) di2[u] = x;
23 }
24 void dfs1(int u,int fa) {
25     int sz = g[u].size();
26     di1[u] = di2[u] = 0;
27     for (int i = 0; i < sz; i++) {
28         int v = g[u][i];
29         if (v == fa) continue;
30         dfs1(v,u);
31         update(di1[v] + 1,u,v);
32     }
33     dia[u] = di1[u] + di2[u];
34 }
35 void dfs2(int u,int fa,int up) {
36     int sz = g[u].size();
37     for (int i = 0; i < sz; i++) {
38         int v = g[u][i];
39         if (v == fa) continue;
40         int tp = max((fo[u] == v ? di2[u] : di1[u]) , up ) + 1;
41         mxdia = max(mxdia, di1[v] + tp);
42         dfs2(v,u,tp);
43     }
44 }
45 void solve(){
46     dfs1(1,-1);
47     mxdia = dia[1];
48     dfs2(1,-1,0);
49     an += mxdia;
50 }
51 int main(){
52     int c;
53     freopen("input.txt","r",stdin);
54     freopen("output.txt","w",stdout);
55     while (~scanf("%d",&c)) {
56         an = 0;
57         while (c--) {
58             scanf("%d",&n);
59             for (int i = 0; i <= n; i++) g[i].clear();
60             for (int i = 0; i < n-1; i++) {
61                 int u,v; scanf("%d%d",&u,&v);
62                 g[u].pbk(v); g[v].pbk(u);
63             }
64             solve();
65         }
66         printf("%d\n",an);
67     }
68     return 0;
69 }
View Code

 

 CF 212E 

题意:一棵树上放两种餐厅,不同的餐厅不能相邻,每种餐厅至少一个,问最多能放几个,并且输出所有可行方案;

分析:显然对于一棵上来说,肯定可以选出一个节点,它有至少两个分支,所以最多的放置数为n-1

对于方案数,我们可以枚举结点,然后对于至少有两个分支的结点进行背包,求出第一中餐厅能放的值,那么所有答案并就是方案;

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<cstdlib>
 7 #include<vector>
 8 #define pbk push_back
 9 using namespace std;
10 const int N = 50000+10;
11 vector<int> g[N];
12 int n;
13 int an[N];
14 int dp[N];
15 
16 void dfs1(int u,int fa) {
17     int sz = g[u].size();
18     dp[u] = 1;
19     for (int i = 0; i < sz; i++) {
20         int v = g[u][i];
21         if (v == fa) continue;
22         dfs1(v,u);
23         dp[u] += dp[v];
24     }
25 }
26 void dfs2(int u,int fa,int up) {
27     int sz = g[u].size();
28     bool tmp[N];
29     memset(tmp,0,sizeof(tmp));
30     tmp[0] = 1;
31     int cnt = 0;
32     for (int i = 0; i < sz; i++) {
33         int v = g[u][i];
34         if (v == fa) continue;
35         cnt++;
36         dfs2(v,u,up + dp[u] - dp[v]);
37         for (int j = n; j >= dp[v]; j--) {
38             if (tmp[j - dp[v]] == 1) tmp[j] = 1;
39         }
40     }
41     for (int j = n; j >= up; j--) {
42         if (tmp[j - up]) tmp[j] = 1;
43     }
44     if (up == 0 && cnt == 1) return;
45     for (int j = n; j >= 0; j--) {
46         if (tmp[j]) an[j] = 1;
47     }
48 }
49 void solve() {
50     memset(an,0,sizeof(an));
51     dfs1(1,-1);
52 
53     dfs2(1,-1,0);
54     int cnt = 0;
55     for (int i = 1; i < n-1; i++) if (an[i]) cnt++;
56     printf("%d\n",cnt);
57     for (int i = 1; i < n-1; i++) {
58         if (an[i]) printf("%d %d\n",i,n-1-i);
59     }
60 }
61 int main(){
62     while (~scanf("%d",&n)) {
63         for (int i = 0; i <= n; i++) g[i].clear();
64         for (int i = 0; i < n-1; i++) {
65             int u,v; scanf("%d%d",&u,&v);
66             g[u].pbk(v); g[v].pbk(u);
67         }
68         solve();
69     }
70     return 0;
71 }
View Code

 

posted @ 2013-08-18 00:16  Rabbit_hair  阅读(984)  评论(0)    收藏  举报