题解:luogu P2634 [国家集训队]聪聪可可

Posted nanjoln0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解:luogu 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”)。

 

输入输出样例

输入样例#1: 
5
1 2 1
1 3 2
1 4 1
2 5 3
输出样例#1: 
13/25

说明

【样例说明】

13组点对分别是(1,1) (2,2) (2,3) (2,5) (3,2) (3,3) (3,4) (3,5) (4,3) (4,4) (5,2) (5,3) (5,5)。

【数据规模】

对于100%的数据,n<=20000。

题解:

点分板题,但处理每层暴力n^2两两组合的话会T。

考虑将每个数按模3后的结果分类。显然,若两条权为w1,w2的路径组合后为三的倍数,那么只有三种情况:w1%3=0,w2%3=0或w1%3=1,w2%3=2或w1%3=2,w2%3=1。

这样问题就很简单了:处理每层时,统计模3为1,2,0的路径有多少条,然后直接算就好了,这样单层就可以做到O(n)了

代码如下:

技术分享图片
#include<bits/stdc++.h>
#define MAXN 40005
#define INF 1e9
using namespace std;
struct front_star{
    int to,next,w;
}edge[MAXN];
int n,cnt=0,k,mx,root,ans=0,tot=1,siz;
int head[MAXN],sz[MAXN],temp[MAXN],idx[MAXN];
bool vis[MAXN];
int maxn(int a,int b)
{
    return a>b?a:b;
}
int Euclid_GCD(int a, int b) 
{
    return b?Euclid_GCD(b, a%b):a;
}
void addedge(int u,int v,int c)
{
    cnt++;
    edge[cnt].to=v;
    edge[cnt].w=c;
    edge[cnt].next=head[u];
    head[u]=cnt;
}
void findroot(int u,int fa)
{
    sz[u]=1;
    int msz=0;
    for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=fa&&!vis[v])
               {
                     findroot(v,u); 
                     sz[u]+=sz[v];
                  msz=maxn(msz,sz[v]);       
               }
        }
    msz=maxn(msz,siz-sz[u]);
    if(msz<mx)
       {
              mx=msz;
              root=u;
       }       
}
void init()
{
    memset(vis,false,sizeof(vis));
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            addedge(a,b,c);
            addedge(b,a,c);
        }
    siz=n;
    mx=INF;
    findroot(1,1);
}
void dist(int u,int fa)
{
    for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!vis[v]&&v!=fa)
               {
                      tot++;
                      idx[v]=tot;
                      temp[tot]=temp[idx[u]]+edge[i].w;
                      dist(v,u);
               }
        }
}
int count_ans(int u,int val)
{
    tot=1;
    idx[u]=1;
    temp[1]=val;
    dist(u,u);
    int s1=0,s2=0,s3=0;
    for(int i=1;i<=tot;i++)
        {
            if(temp[i]%3==0)
               s3++;
            if(temp[i]%3==1)
               s1++;
            if(temp[i]%3==2)
               s2++;      
        }
    int ret=s3*s3+2*s1*s2-s3;    
    return ret;          
}
void divide(int u)
{
    ans+=count_ans(u,0);
    vis[u]=true;
    for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!vis[v])
               {
                       ans-=count_ans(v,edge[i].w);
                       siz=sz[v];
                       mx=INF;
                       findroot(v,u);
                    divide(root);  
               }
        }
}
int main()
{
    init();
    divide(root);
    int GCD=Euclid_GCD(ans+n,n*n);
    printf("%d/%d\n",(ans+n)/GCD,n*n/GCD);
    return 0;
}
View Code

 



以上是关于题解:luogu P2634 [国家集训队]聪聪可可的主要内容,如果未能解决你的问题,请参考以下文章

P2634 [国家集训队]聪聪可可(点分治做法)

P2634 [国家集训队]聪聪可可(树上启发式合并)

2152. [国家集训队]聪聪可可点分治

P2634 [国家集训队]聪聪可可(树形dp)

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

[BZOJ 2152][Luogu P2634]聪聪可可