P2634 [国家集训队]聪聪可可

  好久好久都没有发博客了!

  不是因为我懒,就是集训的时候腾不出时间来写,真是不好意思!

  集训期间没有发博客的类型题,很快就都会补上!

  先发一篇练练手!

  点分治板子题qwq

  我一开始写的是每一次统计calc时 n暴力枚举,但是后来看题解,发现并不需要,因为一个数%3后就只有三种情况,我们可以直接根据这三种情况的个数来累加答案。  

  并且这道题和往常的点分治不一样,a到b的路径和b到a的路径并不重复,并且两个人同时选择一个点也是有可能的,并且要最简(我一开始都没看于是WA了……)

  嗯嗯上代码:

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 220010
int head[maxn],nxt[maxn],to[maxn],w[maxn],dis[maxn],f[maxn],sz[maxn],num[3];
bool vis[maxn];
int cnt,n,tot,root,sum,ans;
void add(int a,int b,int c)
{
    to[++cnt]=b;
    nxt[cnt]=head[a];
    head[a]=cnt;
    w[cnt]=c;
}
void find_root(int u,int fa)
{
    sz[u]=1;
    f[u]=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa||vis[v]) continue;
        find_root(v,u);
        sz[u]+=sz[v];
        f[u]=max(f[u],sz[v]);
    }
    f[u]=max(f[u],sum-sz[u]);
    if(f[root]>f[u]) root=u;
}
void dfs(int u,int fa,int nowdis)
{
    dis[++tot]=nowdis%3;
    num[dis[tot]]++;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(vis[v]||v==fa) continue;
        dfs(v,u,nowdis+w[i]);
    }    
}
int calc(int u,int nowdis)
{
    tot=0;
    num[0]=num[1]=num[2]=0;
    dfs(u,0,nowdis%3);
    return num[0]*num[0]+num[1]*num[2]*2;
}
void devide(int u)
{
    vis[u]=1;
    ans+=calc(u,0);
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(vis[v]) continue;
        ans-=calc(v,w[i]);
        root=0,f[0]=n,sum=sz[v];
        find_root(v,0);
        devide(root);
    }
}
int gcd(int a,int b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    f[0]=sum=n;
    find_root(1,0);
    devide(root);
    int Min=gcd(ans,n*n);
    printf("%d/%d",ans/Min,n*n/Min);
    return 0;
}

  

原文地址:https://www.cnblogs.com/popo-black-cat/p/10411747.html