喵哈哈村的卢西奥

为了拯救喵哈哈村,这个世界必须要存在英雄。

一名叫做卢西奥的英雄站了出来!他现在面临一个难题:

他被要求将一棵树拆成3份,使得每一份中所有节点的权值和相等。

他希望知道,对于一棵给定的有根树,在选取其中2个非根节点并将它们与它们的父亲节点分开后,所形成的三棵子树的节点权值之和能够两两相等的方案有多少种。

两种方案被看做不同的方案,当且仅当形成方案的2个节点不完全相同。

每个输入文件包含多组输入,在输入的第一行为一个整数T,表示数据的组数。

每组输入的第一行为一个整数N,表示给出的这棵树的节点数。

接下来N行,依次描述结点1~N,其中第i行为两个整数Vi和Pi,分别描述这个节点的权值和其父亲节点的编号。

父亲节点编号为0的节点为这棵树的根节点。

满足3<=N<=100000, |Vi|<=100, T<=10

对于每组输入,输出一行Ans,表示方案的数量。


2
3
1 0
1 1
1 2
4
1 0
1 1
1 2
1 3
1
0
题解

两种情况:

第一种情况,有两个是子树,这个东西用启发式合并/dp去维护一下当前sz[x]=all/3的个数就好了。

第二种情况,只有一个是子树,那么第二段显然是包含了第一段的,那么就存在一个的size=2all/3,一个sz=all/3的情况,统计一下就好了。

dfs处理一下就好了,具体看代码:

#include<bits/stdc++.h>
// #include <unordered_set>

using namespace std;

#define FF first
#define SS second
#define MP make_pair
#define PB push_back
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define FOR(i, n, m) for(int i = n; i <= m; i++)
#define REP(i, n, m) for(int i = n; i >= m; i--)
#define ll long long

typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL, LL> PLL;
typedef unsigned long long ULL;

/****************************define***************************************/
const int mod = 1e9 + 7;
const int maxn = 100010;


int n;
int v[maxn], sz[maxn], p[maxn];
vector<int> nd[maxn];
int s;
int cn = 0, cn2 = 0;
ll ans = 0;

void Dfs_sz(int u){
        sz[u] += v[u];
        int t = nd[u].size();
        FOR(i, 0, t-1) {
            Dfs_sz(nd[u][i]);
            sz[u] += sz[nd[u][i]];
            }
        }
void Dfs(int u){
        //printf("node %d. ans %d. cn %d. cn2 %d. sz %d
", u, ans, cn, cn2, sz[u]);
        if(sz[u] == s/3) {
            ans += cn + cn2;
            //printf("node %d. ans %d
", u, ans);
            }
        if(p[u] != 0 && sz[u] == s*2/3) cn2++;
        int t = nd[u].size();
        FOR(i, 0, t-1){
            Dfs(nd[u][i]);
            }
        if(sz[u] == s/3) cn++;
        if(p[u] != 0 && sz[u] == s*2/3) cn2--;
        }

int main(){
        int t;
        cin >> t;
        while(t--){
            cn = 0, cn2 = 0;
            s = 0, ans = 0;
            FOR(i, 0, maxn-1) nd[i].clear();
            memset(sz, 0, sizeof(sz));

            int n, root=-1;
            cin >> n;
            FOR(i, 1, n) {
                cin >> v[i] >> p[i];
                s += v[i];
                nd[p[i]].PB(i);
                if(p[i] == 0) root=i;
                }
            if(s % 3 != 0) cout << 0 << endl;
            else{
                Dfs_sz(root);
                Dfs(root);
                printf("%lld
", ans);
                }
            }
        return 0;
        }
 
原文地址:https://www.cnblogs.com/gfdybz/p/6558850.html