[BZOJ2151]聪聪可可(点分治)

第一次♂点分治

由爸爸♂ljx在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。

代码:

#include
#include
using namespace std;
struct point
{
    int dis;
    int to;
    int next;
}e[100010];
int n,num,ans,root,sum;
int t[4],head[50010],son[50010],f[50010],d[50010];
bool vis[50010];
int gcd(int a,int b)
{
    if(!b) return a;
    else return gcd(b,a%b);
}
void add(int from,int to,int dis)
{
    e[++num].next=head[from];
    e[num].to=to;
    e[num].dis=dis;
    head[from]=num;
}
void getroot(int x,int fa)
{
    son[x]=1;
    f[x]=0;
    for(int i=head[x];i!=0;i=e[i].next)
    {
        int to=e[i].to;
        if(to==fa||vis[to]) continue;
        getroot(to,x);
        son[x]+=son[to];
        f[x]=max(f[x],son[to]);
    }
    f[x]=max(f[x],sum-son[x]);
    if(f[x]int x,int fa)
{
    t[d[x]]++;
    for(int i=head[x];i!=0;i=e[i].next)
    {
        int to=e[i].to;
        if(to==fa||vis[to]) continue;
        d[to]=(d[x]+e[i].dis)%3;
        getdeep(to,x);
    }
}
int cal(int x,int cost)
{
    t[0]=t[1]=t[2]=0;
    d[x]=cost;
    getdeep(x,0);
    return t[1]*t[2]*2+t[0]*t[0];
}
void solve(int x)
{
    ans+=cal(x,0);
    vis[x]=true;
    for(int i=head[x];i!=0;i=e[i].next)
    {
        int to=e[i].to;
        if(vis[to]) continue;
        ans-=cal(to,e[i].dis);
        root=0;
        sum=son[to];
        getroot(to,0);
        solve(root);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        z%=3;
        add(x,y,z);
        add(y,x,z);
    }
    f[0]=sum=n;
    getroot(1,0);
    solve(root);
    int t=gcd(ans,n*n);
    printf("%d/%d\n",ans/t,n*n/t);
    return 0;
}

你可能感兴趣的:(点分治)