[bzoj2152]:聪聪可可(点分治)[洛谷P2634]

这是我的点分治第二题,居然自己写1A了!好开心!好开心!好开心!
题目传送门
和上一个题思路一样,每次找重心,统计经过重心的答案,然后再减去子树中的重复的路径,就是答案了。
关于统计,可以dfs子树中所有节点到根的距离,然后输出%3==0的,%3==1的,%3==2的,然后 sum=cnt0cnt0+cnt1cnt22 ,自己想一想,很好懂的。
关于重复路径还是那张图:
这里写图片描述
不多说了,上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
    int x=0;char ch=' ';int f=1;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct edge{
    int to,next,w;
}e[40001];
int n,tot,root,nowsize,mn=0x3f3f3f3f;
int head[20001];
inline void addedge(int x,int y,int l){
    e[++tot].to=y;e[tot].next=head[x];e[tot].w=l;head[x]=tot;
}
int dis[20001],vis[20001],size[20001];
void getroot(int x,int fa){
    size[x]=1;int num=0;
    for(int i=head[x];i;i=e[i].next){
        int u=e[i].to;
        if(vis[u]||u==fa)continue;
        getroot(u,x);
        size[x]+=size[u];
        num=max(num,size[u]);
    }
    num=max(num,nowsize-size[x]);
    if(num<mn){
        mn=num;root=x;
    }
}
int q[20001];
int l,r;
void getdis(int x,int fa){
    q[++r]=dis[x];
    for(int i=head[x];i;i=e[i].next){
        int u=e[i].to;
        if(vis[u]||u==fa)continue;
        dis[u]=dis[x]+e[i].w;
        getdis(u,x);
    }
}
int calc(int x,int v){
    dis[x]=v;r=0;
    getdis(x,0);
    l=1;
    int cnt0=0,cnt1=0,cnt2=0;
    for(int i=l;i<=r;i++){
        if(q[i]%3==0)cnt0++;
        else if(q[i]%3==1)cnt1++;
        else cnt2++;
    }
    int sum=0;
    sum+=cnt0*cnt0+cnt1*cnt2*2;
    return sum;
}
int ans;
void dfs(int x){
    ans+=calc(x,0);
    vis[x]=1;
    for(int i=head[x];i;i=e[i].next){
        int u=e[i].to;
        if(vis[u])continue;
        ans-=calc(u,e[i].w);
        nowsize=size[u];
        mn=0x3f3f3f3f;
        getroot(u,0);
        dfs(root);
    }
}
int gcd(int a,int b){
    if(b==0)return a;
    return gcd(b,a%b);
}
int main(){
    n=read();
    for(int i=1;i<n;i++){
        int x=read(),y=read(),l=read();
        addedge(x,y,l);addedge(y,x,l);
    }
    nowsize=n;
    getroot(1,0);
    dfs(root);
    int down=n*n;
    int up=ans;
    int t;
    while((t=gcd(down,up))!=1){
        down/=t;up/=t;
    }
    printf("%d/%d",up,down);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值