P2634 [国家集训队]聪聪可可 - 点分治换根 or 树形dp

题目链接;https://www.luogu.com.cn/problem/P2634
题目大意:
在这里插入图片描述
思路:树形dp的题解:https://blog.csdn.net/qq_21433411/article/details/90180495

如果我们用点分治。换根统计之前的子树dp[i]表示之前子树路径%3=i的路径条数。tem[i]目前这棵树路径%3=i的路径条数;然后组合一下就可以了。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=20005, inf=1e9+1000;

struct edg{
    int to, w, next;
}e[N*4];

int tot, root, allnode, n, m;

LL ans=0;
LL dp[3]={0};
LL tem[3]={0};

int head[N], vis[N], siz[N], d[N];
int deep[N];//路径长度//deep[0]子节点个数
int f[N];//求重心的最多子节点个数
int q[N];

void add(int u, int v, int w){
    e[tot].to=v, e[tot].next=head[u];
    e[tot].w=w, head[u]=tot++;
}

void getroot(int u, int fa){//求重心

    siz[u]=1;
    f[u]=0;
    for(int i=head[u]; i!=-1; i=e[i].next){
        int to=e[i].to;
        if(to==fa||vis[to]){
            continue;
        }
        getroot(to, u);
        siz[u]+=siz[to];
        f[u]=max(f[u], siz[to]);
    }
    f[u]=max(allnode-siz[u], f[u]);
    if(f[u]<f[root]){
        root=u;
    }
}

void getdeep(int u, int fa){//获取子树所有节点与根的距离
    tem[d[u]%3]++, d[u]%=3;
    for(int i=head[u]; i!=-1; i=e[i].next){
        int to=e[i].to;
        if(to==fa||vis[to]){
            continue;
        }
        d[to]=d[u]+e[i].w;
        d[to]%=3;
        getdeep(to, u);
    }
}

void cal(int u){//计算当前以重心x的子树下,所有情况的答案

    d[u]=0;
    dp[0]=1;
    for(int i=head[u]; i!=-1; i=e[i].next){
        int to=e[i].to;
        if(vis[to]){
            continue;
        }
        d[to]=e[i].w;
        getdeep(to, u);
        for(int i=0; i<3; i++){
            for(int j=0; j<3; j++){
                if((i+j)%3==0){
                    ans+=dp[i]*tem[j];
                }
            }
        }
        for(int i=0; i<3; i++){
            dp[i]+=tem[i];
        }
        memset(tem, 0, sizeof(tem));
    }
    memset(dp, 0, sizeof(dp));
}

void work(int u){//以x为重心进行计算
    vis[u]=1;
    cal(u);
    for(int i=head[u]; i!=-1; i=e[i].next){
        int to=e[i].to;
        if(vis[to]){
            continue;
        }
        allnode=siz[to];//继续分治
        root=0;
        getroot(to, u);
        work(root);
    }
}

int main(){

    int u, v, w;
    while(~scanf("%d", &n)){
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        tot=1;
        for(int i=1; i<=n-1; i++){
            scanf("%d%d%d", &u, &v, &w);
            add(u, v ,w), add(v, u, w);
        }
        root=0;
        allnode=n, f[0]=inf;
        getroot(1, 0);
        work(root);
        ans*=2;
        ans+=n;
        LL ANS=(n*n);
        LL gcd=__gcd(ANS, ans);
        ans/=gcd, ANS/=gcd;
        printf("%lld/%lld\n", ans, ANS);

    }

    return 0;
}

发布了374 篇原创文章 · 获赞 22 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_21433411/article/details/103507105
今日推荐