洛谷P2634 聪聪可可 (点分治)

###题目链接###

题目大意:

给你一棵树,假如树上两点间的距离是 3 的倍数 的点对有 s 对,则输出最简分数  s/n ,其中 n 表示所有整棵树的点对总数。

分析:

1、显然,可以采用点分治。

2、当然考虑到数据过大,点分治中求距离时,可以不需要把真实距离依次存入 dis[] 数组中。可以将每个距离值 %3  ,这样如果有两个距离 x    y ,若使 x + y 为 3 的倍数,只需要满足两点:

  • x%3==0  &&  y%3==0 
  • x%3==1  &&  y%3==2

上述 x   y 可交换。故只需要用 vis[0] 、vis[1] 、 vis[2] 来标记距离,然后求和即可。

代码如下:

#include<iostream>
#include<algorithm>
#include<string.h>
#define maxn 20008
#define inf 0x3f3f3f3f
using namespace std;
bool vis[maxn];
int n,cnt,root,size,ans;
int head[maxn],sz[maxn],f[maxn],flag[4];
struct Edge{
    int to;
    int val;
    int next;
}edge[maxn<<1];
inline void add(int u,int v,int w){
    edge[++cnt].to=v;
    edge[cnt].val=w;
    edge[cnt].next=head[u];
    head[u]=cnt;
    return;
}
int gcd(int a,int b){
    if(!b) return a;
    return gcd(b,a%b);
}
void  getdis(int u,int pre,int d){
    for(int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre||vis[v]) continue;
        flag[(d+edge[i].val)%3]++;
        getdis(v,u,d+edge[i].val);
    }
    return;
}
void calc(int u,int d,int s){
    flag[0]=flag[1]=flag[2]=0;
    flag[d%3]++;
    getdis(u,-1,d);
    ans+=s*flag[0]*(flag[0]-1);
    ans+=s*flag[1]*flag[2]*2;
    return;
}
void  getroot(int u,int pre){
    sz[u]=1,f[u]=0;
    for(int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre||vis[v]) continue;
        getroot(v,u);
        sz[u]+=sz[v];
        f[u]=max(f[u],sz[v]);
    }
    f[u]=max(f[u],size-sz[u]);
    if(f[u]<f[root]) root=u;
    return;
}
void divide(int u){
    vis[u]=true;
    calc(u,0,1);
    int sum=size;
    for(int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if(vis[v]) continue;
        calc(v,edge[i].val,-1);
        size=sz[v]>sz[u]?sum-sz[u]:sz[v];
        root=0;
        getroot(v,-1);
        divide(root);
    }
    return;
}
int main()
{
    scanf("%d",&n);
    int x,y,w;
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&x,&y,&w);
        add(x,y,w),add(y,x,w);
    }
    root=0;
    f[root]=inf;
    size=n;
    getroot(1,-1);
    divide(root);
    ans+=n;
    int t=n*n;
    int g=gcd(ans,t);
    ans/=g,t/=g;
    printf("%d/%d\n",ans,t);
}

猜你喜欢

转载自www.cnblogs.com/Absofuckinglutely/p/11945974.html