[国家集训队]聪聪可可

题意

Here

思考

最近学习一下点分治

本题点分治裸题,也可以用树形 \(dp\) 做,在此记录一下点分治的做法:

首先题目要求求出边权和为 \(3\) 的倍数的路径个数和,那么我们可以将路径和对 \(3\) 取模,树上路径就只分为了三种: \(0,\ 1,\ 2\),用一个桶记录个数,那么每次点分治计算的答案就是 \(sum[0] * sum[0] + sum[1] * sum[2]\),(两条路径为\(0\)的链和一条路径为\(1\)的链\(+\)一条路径为\(2\)的链,由于后者的起点和中点的位置可以互换,所以就是乘法原理~)

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 20020;
struct node{
    int nxt, to, dis;
}edge[N << 1];
int head[N], num;
void build(int from, int to, int dis){
    edge[++num].nxt = head[from];
    edge[num].to = to;
    edge[num].dis = dis;
    head[from] = num;
}
int dist[N], sz[N], vis[N], f[N], sum, root, ans[5], ANS;
void getroot(int u, int fa){
    sz[u] = 1; f[u] = 0;
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(v == fa || vis[v]) continue;
        getroot(v, u);
        sz[u] += sz[v];
        f[u] = max(f[u], sz[v]);
    }
    f[u] = max(f[u], sum - sz[u]);
    if(f[u] < f[root]) root = u;
}
void getdep(int u, int fa){
    dist[u] %= 3;
    ans[dist[u]] ++;
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(v == fa || vis[v]) continue;
        dist[v] = dist[u] + edge[i].dis;
        getdep(v, u);
    }
}
int calc(int u, int nowd){
    dist[u] = nowd;
    ans[0] = ans[1] = ans[2] = 0;
    getdep(u, 0);
    return ans[0] * ans[0] + ans[1] * ans[2] * 2;
}
void solve(int u, int fa){
    ANS += calc(u, 0); vis[u] = 1;
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(v == fa || vis[v]) continue;
        ANS -= calc(v, edge[i].dis);
        root = 0;
        sum = sz[v];
        getroot(v, u);
        solve(root, 0);
    }
}
int n;
int gcd(int x, int y){
    if(y == 0) return x;
    return gcd(y, x % y);
}
int main(){
    cin >> n;
    for(int i=1; i<=n-1; i++){
        int u, v, d;
        cin >> u >> v >> d;
        build(u, v, d); build(v, u, d);
    }
    sum = n; f[0] = 0x3f3f3f3f; root = 0;
    getroot(1, 0);
    solve(root, 0);
    int GCD = gcd(ANS, n * n);
    cout << ANS / GCD << "/" << n * n / GCD;
    return 0;
}

总结

点分治多注意一下细节就好了,记得考虑两个点在一个子树内的情况要减掉

posted @ 2018-11-23 23:02  alecli  阅读(92)  评论(0编辑  收藏  举报