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 ",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 }
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 ",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 }
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 ",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 }
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 ",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 }
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 ",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 */
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 ",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 ? ' ' : ' '); 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 }
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 ",an); 67 } 68 return 0; 69 }
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 ",cnt); 57 for (int i = 1; i < n-1; i++) { 58 if (an[i]) printf("%d %d ",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 }