题目描述

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

他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画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。

题解:
树形DP,f[i][x]表示以i为根的子树,到i的距离%3为x的路径数。

每搜完一个点,就与它的同辈更新一下答案。然后更新一下根节点的f值。

记得最后ans=ans*2+n;因为反过来也是一种答案,并且n个点可以单独成为一个答案,即(n,n)。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
template<typename T>void read(T &x)
{
x=;char c=getchar();
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())x=x*+c-'';
}
int head[],size,n,ans;
struct node
{
int next,to,dis;
}edge[];
void putin(int from,int to,int dis)
{
size++;
edge[size].to=to;
edge[size].dis=dis;
edge[size].next=head[from];
head[from]=size;
}
int f[][];
void dfs(int r,int fa)
{
int i;
for(i=head[r];i!=-;i=edge[i].next)
{
int y=edge[i].to;
if(y!=fa)
{
dfs(y,r);
ans+=f[y][-edge[i].dis%]*f[r][]+f[y][(edge[i].dis%<=)?(edge[i].dis%)^:]*f[r][]+f[y][(edge[i].dis%==)?:(edge[i].dis%)^]*f[r][];
f[r][edge[i].dis%]+=f[y][];
f[r][(edge[i].dis+)%]+=f[y][];
f[r][(edge[i].dis+)%]+=f[y][];
}
}
ans+=f[r][];
f[r][]++;
}
int gcd(int a,int b)
{
if(b==)return a;
else return gcd(b,a%b);
}
int main()
{
int i,j;
read(n);
memset(head,-,sizeof(head));
for(i=;i<n;i++)
{
int from,to,dis;
read(from);read(to);read(dis);
putin(from,to,dis);
putin(to,from,dis);
}
dfs(,);
ans=ans*+n;
int k=gcd(ans,n*n);
printf("%d/%d",ans/k,n*n/k);
return ;

点分治代码:

 #include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
struct node{
int next,to,dis;
}edge[];
int head[],size=;
void putin(int from,int to,int dis){
size++;
edge[size].to=to;
edge[size].dis=dis;
edge[size].next=head[from];
head[from]=size;
}
int root,sum,son[],cnt[],vis[];
void getroot(int r,int fa){
int i;
cnt[r]=;
son[r]=;
for(i=head[r];i!=-;i=edge[i].next){
int y=edge[i].to;
if(y!=fa&&!vis[y]){
getroot(y,r);
cnt[r]+=cnt[y];
if(cnt[y]>son[r])son[r]=cnt[y];
}
}
son[r]=max(son[r],sum-cnt[r]);
if(son[r]<son[root])root=r;
}
int ans,dist[],t[];
void getdeep(int r,int fa){
int i;
t[dist[r]]++;
for(i=head[r];i!=-;i=edge[i].next){
int y=edge[i].to;
if(y!=fa&&!vis[y]){
dist[y]=(dist[r]+edge[i].dis)%;
getdeep(y,r);
}
}
}
int getans(int r,int len){
t[]=t[]=t[]=;
dist[r]=len%;
getdeep(r,);
return t[]*t[]*+t[]*t[];
}
void solve(int r){
ans+=getans(r,);
vis[r]=;
for(int i=head[r];i!=-;i=edge[i].next){
int y=edge[i].to;
if(!vis[y]){
ans-=getans(y,edge[i].dis);
root=;
sum=cnt[y];
getroot(y,);
solve(root);
}
}
}
int gcd(int a,int b){
if(b==)return a;
else return gcd(b,a%b);
}
int main(){
int i,j;
memset(head,-,sizeof(head));
scanf("%d",&n);
for(i=;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
c%=;
putin(a,b,c);
putin(b,a,c);
}
root=;
son[]=sum=n;
getroot(,);
solve(root);
int t=gcd(ans,n*n);
printf("%d/%d\n",ans/t,n*n/t);
return ;
}

[luogu 2634]聪聪可可的更多相关文章

  1. 洛谷 2634&&BZOJ 2152: 聪聪可可【点分治学习+超详细注释】

    2152: 聪聪可可 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 3435  Solved: 1776[Submit][Status][Discuss ...

  2. Luogu 4206 [NOI2005]聪聪与可可

    BZOJ 1415 简单期望 + 记忆化搜索. 发现聪聪每一步走向的地方是在可可的所在位置确定时是确定的,设$nxt_{x, y}$表示聪聪在$x$,可可在$y$时聪聪下一步会走到哪里,我们先预处理出 ...

  3. luogu P2634 [国家集训队]聪聪可可 点分治

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

  4. luogu P4206 [NOI2005]聪聪与可可 期望dp 记忆化搜索

    LINK:聪聪与可可 这道题的核心是 想到如何统计答案. 如果设f[i][j]表示第i个时刻... 可以发现还需要统计位置信息 以及上一次到底被抓到没有的东西 不太好做. 两者的位置都在变化 所以需要 ...

  5. luogu P4206 聪聪和可可

    聪聪和可可 鸽了两天 \(dijkstra\)预处理出来两点之间的最短路径\(dis\)数组,读题发现,\(cat\)的走位很怪sb斩了,所以我们设一个\(next\)数组,\(next[i][j]\ ...

  6. 【P2634】聪聪可可——点分治

    (题面来自Luogu) 题目描述 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)--遇到这种问题,一般情况下石头 ...

  7. [bzoj2152][聪聪和可可] (点分治+概率)

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

  8. BZOJ 1415 【NOI2005】 聪聪和可可

    题目链接:聪聪和可可 一道水题--开始还看错题了,以为边带权--强行\(O(n^3)\)预处理-- 首先,我们显然可以预处理出一个数组\(p[u][v]\)表示可可在点\(u\),聪聪在点\(v\)的 ...

  9. 【bzoj1415】 Noi2005—聪聪和可可

    http://www.lydsy.com/JudgeOnline/problem.php?id=1415 (题目链接) 题意 一张图,聪聪想吃可可.每单位时间聪聪可以先移动两次:可可后移动一次或停在原 ...

随机推荐

  1. MySQL业务-发放的优惠券 用户使用情况_20161028

    运营部门给用户发放优惠券,如果想监控优惠券的使用效果 优惠券使用率是个反映效果的很好指标 下面sql就是针对某天对特定用户发放的优惠券在发放日期以后每天的使用情况 SELECT e.城市,e.用户ID ...

  2. poj1417 True Liars[并查集+背包]

    有一点小转化的题,在设计dp状态时还是有点费脑筋的. 地址. 依题意,首先可以知道肯定要扩展域的并查集(明摆着的嘛).一个"好人"域,一个"坏人"域,每句话分两 ...

  3. C# GUID使用总结

    全局唯一标识符(GUID,Globally Unique Identifier) What is GUID 也称作 UUID(Universally Unique IDentifier) . GUID ...

  4. 利用反射拿到并递归C#类中的各个字段名字及类型

       以下方法实现了遍历一个class中所有的字段, 并且递归遍历sub class.  private StringBuilder _properties = new StringBuilder() ...

  5. window下redis如何查看版本号

    1.启动服务端:redis-server 2.启动客户端:redis-cli 3.客户端输入:info 即可.

  6. 项目清理和删除svn信息(转)

    svn信息是根据文件夹里面的 .svn 文件夹保存相关配置的,删除此文件夹即可去掉svn信息: linux下: find . -type d -name ".svn"|xargs ...

  7. URL shortening service

    Use Cases 1, shortening : take a URL => return a much shorter URL 2, redirection : take a short U ...

  8. EasyUI把datagrid的值赋给表单

    $('#infoForm${INDEX}').form('load', rowToDto(pageConfig${INDEX}.infoName, row)); function rowToDto(i ...

  9. R 中数据导入

    R语言数据导入  数据导入 1.保存和加载R的数据(与R.data的交互:save()函数和load()函数) a <- 1:10 save(a, file = "data/dumDa ...

  10. jsp九大内置对象与servlet中java对象

    jsp九大内置对象 request对象 :  getParameter(String name)获取表单提交的数据 getParamegerNames() 获取客户端提交的所有参数名 getAttri ...