题解: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;
}

原文地址:https://www.cnblogs.com/nanjolno/p/9028288.html

时间: 2025-01-10 09:09:44

题解:luogu P2634 [国家集训队]聪聪可可的相关文章

【题解】P1407国家集训队稳定婚姻

[题解][P1407 国家集训队]稳定婚姻 很好的一道建模+图论题. 婚姻关系?很像二分图匹配呀,不过不管怎么办先建模再说.婚姻关系显然用图方面的知识解决.建图! 它给定的是字符串,所以我们使用\(ac\)自动机\(map\)作匹配建点. 题意就是给你\(n\)对夫妻关系和\(m\)对情人关系,已知情人关系都可以结婚,现在假设对于某个婚姻,如果离婚,这\(2n\)个人最终依然能够结合成\(n\)对情侣,那么这样的婚姻是不稳定的.现在问每个婚姻关系的稳定性. 考虑什么样的婚姻关系是不稳定的.题目给

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

题意:求树上路径权值和为3的倍数的路径条数 总结:f[0],f[1],f[2]表示权值%3后为0,1,2的点数 然后直接点分统计就好了 #include<bits/stdc++.h> using namespace std; const int maxn = 200005; int n, head[maxn], cnt = 1, tot, f[maxn], dep[maxn], d[maxn]; struct Node{ int v, nxt, w; } G[maxn]; int root,

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

题意:给你一颗树,询问路径和是3的倍数的路径有多少条 思路:日常搬运点分治,这个题其实是运用了取模,三的倍数其实可以转化为对3取模,然后直接点分治 代码:(一直T,最后发现是getroot时写错了) #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int read() { int x=0,w=1;char ch=getchar(); while ((ch<

【luogu P1494 [国家集训队]小Z的袜子】 题解

题目链接:https://www.luogu.org/problemnew/show/P1494 #include <cstdio> #include <algorithm> #include <iostream> #include <cmath> using namespace std; const int maxn = 50000+10; inline long long read() { long long k=0; char c; c=getchar

LUOGU P1505 [国家集训队]旅游 (树链剖分+线段树)

传送门 解题思路 快被调死的码农题,,,其实就是一个边权下放到点权的线段树+树剖. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; const int MAXN = 100005; const int inf = 0x3f3f3f3f; inline int rd(){

LUOGU P1407 [国家集训队]稳定婚姻

传送门 解题思路 让所有夫妇之间连男到女的边,所有情人之间连女到男的边,然后用$tarjan$,如果对于一对夫妻在强连通分量里,那么就是不稳定的,因为他们可以绕一圈. #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<map> using namespace std; const int MAXN = 8005; const int MA

【拓展卢卡斯】LuoGu P2183 [国家集训队]礼物

这是一道CTSC水题 题目描述 一年一度的圣诞节快要来到了.每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物.不同的人物在小E心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多.小E从商店中购买了n件礼物,打算送给m个人,其中送给第i个人礼物数量为wi.请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某个人在这两种方案中收到的礼物不同).由于方案数可能会很大,你只需要输出模P后的结果. 输入输出格式 输入格式: 输入的第一行包含一个正整数P,表示模: 第二行包

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

[BZOJ 2152][Luogu P2634]聪聪可可 <题意概括> 给定一棵树,求树上距离为3的倍数的点对对数 <做法> 有关树上路径统计的问题当然可以想到点分治 与普通点分治一样的做法 在统计路径时使用$Ans_{i}$来储存$Length\equiv i(mod3)$的路径条数 则易知答案数为$Ans_{0}^{2}+2*Ans_{1}*Ans_{2}$$(0+0\equiv 1+2\equiv 2+1\equiv 0(mod3))$ <Code> #inclu

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

Description 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏.他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树).并且每条“边”上都有一个数.接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所