感觉是一道很妙的树形DP题,充分利用到了树的性质(虽然说点分治也可以做,,,,但是本蒟蒻不会啊)

然而某Twilight_Sx大佬表示这道题真的非常水,,,本蒟蒻也只能瑟瑟发抖了

本蒟蒻表示还是要经过一些思考的吧

虽然说是要获取概率,但是要输出分数形式,显然直接算可能获胜的次数再除所有可能,并且用gcd约分会更好,

对于每个点而言,要么做中转(LCA),要么是其中一个点,由于是树,统计这些点对可以保证不重不漏,

所以枚举到每一个点时,f[i][j],表示在i的子树中,到i这个节点距离为j的个数,

如果这个点是其中一个点,那么任意一个子树对它的贡献是能够和它到对应儿子的距离互补的节点个数,

也就是说设点i到它的某个儿子k的距离为t(mod 3),那么这个子树对ans的贡献是f[k][3-t].

同时因为f[k][j]不包括k,所以要特判加入i,k这个点对

如果这个点是中转(LCA),那么每次枚举到一个子树时,这个子树对它的贡献是:

设当前节点到这个儿子k的距离为t(mod 3),

那么由于每次枚举后都会把子树对f[i]的贡献统计进来,所以当前的f[i][j]就是之前枚举到的子树里,

到当前节点i的距离为j的点的个数,因此对于现在枚举到的这个子树,依次枚举0~2(j的大小)

那么f[k][j]的点对应到之前的子树就应该要对应到3-(j+t)%3上,因为这样加起来才是3的倍数,

(因为这个中转到f[k][j]中的点的距离是(j+t)%3 (mod 3)),

并且跟普通枚举防止重复同理,每棵子树只统计它和之前就枚举到的子树里的点对,就可以防止一个点对重复被枚举到,

但是这样统计的话,由于每次是统计新增子树对之前子树的贡献,而f[i][j]代表的是子树中的,而不包括自己,

因此对于i而言,它做中转,枚举到子树k时,它之前的子树里面可选的都已经包括到f[i]当中了,

但是f[k]本来就是不包括k的,所以就会遗漏点k到i之前的子树中的点对的贡献,因此就要特判加入。

判断加入距离为t(mod 3)时,f[i][3-t]即可,但是对于t == 0,那么对面肯定不能选j == 3,

所以这个要特判,如果t == 0,那么加入的是f[i][0],同理,对于之前统计子树的情况,

(f[k][j]中(j+t)%3 也可能 == 0,所以这个时候要加入f[i][0])

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 40400
int n;
int date[AC],Next[AC],Head[AC],tot,value[AC];
int f[AC][],ans;
bool z[AC];
inline int read()
{
int x=;char c=getchar();
while(c>'' || c<'') c=getchar();
while(c>='' && c<='') x=x*+c-'',c=getchar();
return x;
} inline void add(int f,int w,int S)
{//因为不知道哪个是根,所以要加双向边
date[++tot]=w,Next[tot]=Head[f],value[tot]=S,Head[f]=tot;
date[++tot]=f,Next[tot]=Head[w],value[tot]=S,Head[w]=tot;
} inline int gcd(int x,int y)
{
int t;
while(y)
{
t=x%y;
x=y;
y=t;
}
return x;
} void pre()
{
R a,b,c;
n=read();
for(R i=;i<n;i++)
{
a=read(),b=read(),c=read();
add(a,b,c);
}
} void DFS(int x)
{
R k;
z[x]=true;
for(R i=Head[x]; i ;i=Next[i])
{
k=date[i];
if(z[k]) continue;//跳过父亲
DFS(k);
int t=value[i]%;
if(!t)
{
ans++;//如果直接就是一个点对,那就加上
ans+=f[k][];//并且加上距离为0的点
}
else ans+=f[k][-t];//不然取互补点
//以上是x为点对中的一个点的情况,以下为中转
if(!t)//因为x做中转时,k还没统计进来,所以要特判k与之前子树所形成的点对
ans+=f[x][];//如果t是0的话,就直接加距离为0的点就可以了
else ans+=f[x][-t];//不然加互补的
for(R j=;j<;j++)
{
int go= - (t + j) % ;//到儿子的距离为j,那么到x的距离就为(t+j)%3,所以互补就是3 - (t + j) % 3;
if(go == ) go=;//如果go为3,那么实际距离应该是0,不过貌似可以通过对go取mod的方式避免特判?
ans+=f[k][j] * f[x][go];
}
for(R j=;j<;j++)//error!!!统计入f这种事应该在统计完ans之后才可以做,不然就无法保证当前f[x]里面一定是之前的子树了
f[x][(t+j)%]+=f[k][j];//到k的距离为j,那么实际应该贡献给f[x][(t+j)%3](算上儿子的子树)
f[x][t]++;//算上儿子
}
} void work()
{
DFS();
//printf("%d\n",ans);
ans=ans*+n;//两个点不重合的方案可以互换--->*2,可以两个人选一个点--->+n
int k=n*n,g=gcd(ans,k);//全部方案
printf("%d/%d\n",ans/g,k/g); /*for(R i=1;i<=n;i++)
{
for(R j=0;j<3;j++)
printf("%d ",f[i][j]);
printf("\n");
}*/
}
int main()
{
// freopen("in.in","r",stdin);
pre();
work();
// fclose(stdin);
return ;
}

【国家集训队】聪聪可可 ——树形DP的更多相关文章

  1. luogu2634 聪聪可可 (树形dp)

    要求出两点间距离==0(mod3) 的数量,然后除以(n*n) 设f[i][j]为i的子树到i的距离==j(mod3)的数量,然后做树形dp即可 因为要最简,所以要求一下gcd,然后除下去 #incl ...

  2. BZOJ 2152 聪聪可可(树形DP)

    给出一颗n个点带边权的树(n<=20000),求随机选择两个点,使得它们之间的路径边权是3的倍数的概率是多少. 首先总的对数是n*n,那么只需要统计路径边权是3的倍数的点对数量就行了. 考虑将无 ...

  3. 洛谷 P2634 聪聪可可 —— 树形DP / 点分治

    题目:https://www.luogu.org/problemnew/show/P2634 今天刚学了点分治,做例题: 好不容易A了,结果发现自己写的是树形DP...(也不用找重心)(比点分治快) ...

  4. bzoj2152 聪聪可可 (树形dp)

    大意: 给定树, 随机选两点, 求两点距离是3的倍数的概率. 树形dp入门水题, 枚举每个点作为lca时的答案即可. #include <iostream> #include <qu ...

  5. 【BZOJ】1415 [Noi2005]聪聪和可可 期望DP+记忆化搜索

    [题意]给定无向图,聪聪和可可各自位于一点,可可每单位时间随机向周围走一步或停留,聪聪每单位时间追两步(先走),问追到可可的期望时间.n<=1000. [算法]期望DP+记忆化搜索 [题解]首先 ...

  6. BZOJ 1415 聪聪和可可(期望DP)

    我们可以用n次BFS预处理出 to[][]数组,to[i][j]表示聪聪从i点到j点第一步会走哪个点. 那么对于聪聪在i点,可可在j点,聪聪先走,定义dp[i][j]表示步数期望. 那么显然有dp[i ...

  7. BZOJ 1415 [NOI2005]聪聪与可可 (概率DP+dfs)

    题目大意:给你一个无向联通图,节点数n<=1000.聪聪有一个机器人从C点出发向在M点的可可移动,去追赶并吃掉可可,在单位时间内,机器人会先朝离可可最近的节点移动1步,如果移动一步机器人并不能吃 ...

  8. 洛谷4206/NOI2005T4 聪聪和可可 期望DP+记忆化搜索

    题意:给出n个点m条边的无向图,两个主角聪聪和可可开始分别在S点和T点.聪聪想吃掉可可,每次由匆匆先行动后来可可行动.聪聪的行动是选他到可可的最短路上的点走最多两步(如果最短路有几条就选编号最小的走) ...

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

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

随机推荐

  1. 手机APP测试如何进行兼容性测试?

    Android App兼容性测试是一个比较重要的App评价内容,实际上兼容性测试不仅仅和测试人员相关,在开发阶段就应当着重考虑,因为兼容性问题是除了实现App本身要求的功能后,必须要关注.而且至关重要 ...

  2. appium -- 页面出现弹窗,关闭后,无法识别页面元素

    1. 问题:如图所示:在修改手势密码的过程中,点击了返回按钮后,弹出该弹窗:点击继续设置后,就发现 driver.getPageSource()获取不到页面元素.在找了一圈无用的资料后,没有什么好的处 ...

  3. 破解IDEA注册码,设置 license server一直有效不过期

    破解的详细过程: 1.从下面地址下载一个jar包,名称是  JetbrainsCrack-2.10-release-enc.jar 下载地址是http://idea.lanyus.com/,进去之后点 ...

  4. 项目实战:BBS+Blog项目开发

    01-博客系统之功能需求 02-博客系统之表结构设计1 03-博客系统之表结构设计2 04-博客系统之表结构设计3 05-博客系统之表结构设计4 06-博客系统之表机构设计5 07-博客系统之创建系统 ...

  5. EasyUI学习心得

    因为要修改十几年前的一个项目界面,打9月份开始学习EasyUI,很多事情都要自己试过才知道,小问题会浪费很多时间.所以,就在此记录一下,随时更新. 一.引号 EasyUI的自定义关键字的识别,API文 ...

  6. maven项目中没有resource文件夹的问题

    之前使用eclipse创建maven项目,文件夹都是建好的,这几次创建,都没有resource文件夹,需要手动创建resource. 现象描述 在eclipse中,创建maven项目有两种方式: 一种 ...

  7. IntelliJ IDEA for MAC 注释模板、快捷键生成注释

    增加注释 在IntelliJ IDEA中为JAVA代码增加注释,首先需要配置注释模板,而后使用模板快捷键生成注释, 下面按照[配置模板].[模板使用]两部分进行介绍 ----------------- ...

  8. OpenCV学习4-----K-Nearest Neighbors(KNN)demo

    最近用到KNN方法,学习一下OpenCV给出的demo. demo大意是随机生成两团二维空间中的点,然后在500*500的二维空间平面上,计算每一个点属于哪一个类,然后用红色和绿色显示出来每一个点 如 ...

  9. C中文件操作的文本模式和二进制模式,到底有啥区别?

    在C中,使用fopen打开文件有两种模式:一种是文本模式,一种是二进制模式.那这两种模式之间有什么区别,是不是使用文本模式打开的文件就只能使用文本函数比如fprintf来操作,而使用二进制打开的文件就 ...

  10. 什么是Frozen Binary

    对于Python来说,你可以将Python的字节码,PVM(也就是解析器),以及需要的相关类库,打包成一个package,这个package实际上是一个二进制可执行文件,这样,用户获取到这个packa ...