这几天训练赛的一道小题,参考学长题解写的
题目点击打开链接
/*
题意说,给一棵树,如果一条边去掉之后树划为的两个部分都有偶数个节点,那么这条边可以被去掉,问最多可以去掉多少条边。 在树上呢,如果两条不同的边两侧都有偶数个节点,那么他们在树上一定是不相邻的。。好好想想。。。所以说其实我去掉一条边完全不影响其他可去掉的边的情况,那么其实就是只要这条边可以去掉就把它去掉就好了,这样就变成了统计哪些边左右都有偶数个节点。 对于一条边,它从一个父亲连接到一个儿子,那么以儿子为根的子树上的节点就是这条边其中一边的节点,另一边拿总数n减去就好。所以问题就变成对于每一个节点,统计他的子树上共有多少个节点。 如果这个问题还不会求,就要回想回想树形dp,对于一个根,它对应子树上所有的节点,等于他所有儿子对应子树的节点和加上他自己一个,这个还是好理解吧,写成递归的形式:
cal(u) ans[u] = 1; for every son of u: cal(son); ans[u] += ans[v]; */ #include<iostream> #include<cstdio> #include<string.h> #include<vector> using namespace std; vector<int> v[100005]; int vis[100005]; int node[100005]; int cutedge; void dfs2(int x) { vis[x] = 1; int child; for (int k=0; k<v[x].size(); k++) { child = v[x][k]; if (!vis[child]) { dfs2(child); if (node[child]%2 == 0) cutedge ++; } } } void dfs(int x) { vis[x] = 1; node[x] = 1; int child; for (int k=0; k<v[x].size(); k++) { child = v[x][k];//vector的奇妙用处 if (!vis[child]) { dfs(child); node[x] += node[child]; } } } int main() { int num; scanf("%d", &num); for (int i=0; i<num; i++) { int vnum; scanf("%d", &vnum); int m, n; for (int j=1; j<=vnum; j++) { v[j].clear(); } for (int j=1; j<vnum; j++) { scanf("%d%d", &m, &n); v[m].push_back(n); v[n].push_back(m); } memset(vis, 0, sizeof vis); memset(node, 0, sizeof node); dfs(1);//这个递归循环求出每个子树的结点数 memset(vis, 0, sizeof vis); cutedge = 0; dfs2(1);//这个子循环判断每各节点node是否为偶数,由于总数是偶数,那么只要一边是偶数,另一边肯定也是偶数 printf("%d\n", cutedge); } return 0; }