题意:给定一棵树,每个节点拥有权值,问能否找到两个点,断开它们与父节点的边能使树分成权值和相等的三部分。权值可以为负
思路:进行两遍DFS,第一遍找最深的子树和为sum/3的节点,标记掉找到的点,同时更新剩下点的子树和,第二遍在第一遍的基础上再找一遍就可以了。注意权值可以为负,那么就意味着,虽然找到的两个点为根的子树和都等于sum/3,但不一定使根节点的子树和为sum/3,再判断一次就可以了
/** @Date : 2017-04-18 21:27:36 * @FileName: 767C DFS.cpp * @Platform: Windows * @Author : Lweleth (SoundEarlf@gmail.com) * @Link : https://github.com/Lweleth * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e6+20; const double eps = 1e-8; vectoredg[N]; int val[N]; int a[N]; bool vis[N]; int ans[2]; int sum; int flag; int cnt; int rt; int dfs(int x) { int res = a[x]; for(int i = 0; i < edg[x].size(); i++) { if(!vis[edg[x][i]]) res += dfs(edg[x][i]); } //cout << x << "-" << res << "~" << endl; if(res == sum / 3 && !flag && x != rt) { vis[x] = 1; flag = 1; //cout << x <<"@"; ans[cnt++] = x; res = 0; } return val[x] = res; } int main() { int n; while(~scanf("%d", &n)) { int x, y; rt = -1; ans[0] = ans[1] = -1; sum = 0; for(int i = 0; i <= n; i++) edg[i].clear(), vis[i] = 0; for(int i = 1; i <= n; i++) { scanf("%d%d", &x, &y); a[i] = y; edg[x].PB(i); sum += y; if(x == 0) rt = i; } if(sum % 3 == 1) { printf("-1 "); continue; } cnt = 0; flag = 0; dfs(rt); flag = 0; dfs(rt); if(cnt < 2 || val[rt] != sum / 3)//还要加判根节点的值 因为有可能出现负值节点 printf("-1 "); else printf("%d %d ", ans[0], ans[1]); } return 0; }