洛谷 P2634 [国家集训队]聪聪可可 解题报告

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

题目描述

聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。

他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画\(n\)个“点”,并用\(n-1\)条“边”把这\(n\)个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。

聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。

输入输出格式

输入格式:

输入的第\(1\)行包含\(1\)个正整数\(n\)。后面\(n-1\)行,每行3个整数\(x,y,w\),表示\(x\)号点和\(y\)号点之间有一条边,上面的数是\(w\)

输出格式:

以即约分数形式输出这个概率(即“\(a/b\)”的形式,其中\(a\)\(b\)必须互质。如果概率为\(1\),输出“\(1/1\)”)。

说明:

对于\(100\%\)的数据,\(n \le 20000\)


淀粉质裸题

对按顺序统计每颗子树,拿桶装一装,随便统计一下

注意这样的复杂度是\(O(nlogn)\)


Code:

#include <cstdio>
#include <cstring>
const int N=2e4+10;
const int inf=0x3f3f3f3f;
int head[N],to[N<<1],Next[N<<1],edge[N<<1],cnt;
void add(int u,int v,int w)
{
    to[++cnt]=v,Next[cnt]=head[u],edge[cnt]=w,head[u]=cnt;
}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int siz[N],buct[4],tmp[4],mi,id,del[N],ans,n;
void getroot(int now,int fa,int su)
{
    siz[now]=1;int mx=0;
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(!del[v]&&v!=fa)
        {
            getroot(v,now,su);
            mx=mx>siz[v]?mx:siz[v];
            siz[now]+=siz[v];
        }
    }
    mx=mx>su-siz[now]?mx:su-siz[now];
    if(mx<mi) mi=mx,id=now;
}
void dfs(int now,int fa,int dis)
{
    ++tmp[dis],siz[now]=1;
    ans+=buct[3-dis];
    if(!dis) ans+=buct[0];
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(!del[v]&&v!=fa)
            dfs(v,now,(dis+edge[i])%3),siz[now]+=siz[v];
    }
}
void divide(int now,int su)
{
    mi=inf;
    getroot(now,0,su);
    now=id,del[now]=1;
    memset(buct,0,sizeof(buct));
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(!del[v])
        {
            memset(tmp,0,sizeof(tmp));
            dfs(v,now,edge[i]);
            for(int j=0;j<=2;j++) buct[j]+=tmp[j];
        }
    }
    ans+=buct[0];
    for(int i=head[now];i;i=Next[i])
        if(!del[to[i]])
            divide(to[i],siz[to[i]]);
}
int main()
{
    scanf("%d",&n);
    for(int u,v,w,i=1;i<n;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        w%=3;
        add(u,v,w),add(v,u,w);
    }
    divide(1,n);
    int a=(ans<<1)+n,b=n*n;
    int GCD=gcd(a,b);
    a/=GCD,b/=GCD;
    printf("%d/%d",a,b);
    return 0;
}


2018.9.15

posted @ 2018-09-15 16:37  露迭月  阅读(237)  评论(0编辑  收藏  举报