感觉是一道很妙的树形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. MySQL高级-主从复制

    一.复制的基本原理 1.slave会从master读取binlog来进行数据同步 2.步骤+原理图 二.复制的基本原则 1.每个slave只有一个master 2.每个slave只能有一个唯一的服务器 ...

  2. SpringBoot学习:添加JSP支持

    项目下载地址:http://download.csdn.NET/detail/aqsunkai/9805821 (一)pom中添加依赖: <!-- https://mvnrepository.c ...

  3. Windows运行机理——主程序—WinMain

    Windows运行机理这系列文章都是来至于<零基础学Qt4编程>——吴迪,个人觉得写得很好,所以进行了搬运和个人加工 在windows 操作系统下,用C 或者C++来编写MS-DOS 应用 ...

  4. Sysbench安装步骤及详情

    Sysbench安装步骤及详情 Sysbench是压力测试工具,可以测试系统的CPU,内存,I/O等,也可以用于数据库的性能测试 Sysbench安装步骤: 安装具体分为Ubuntu16.04和Cen ...

  5. Ubuntu14.04 panic --not syncing: Attempt to kill init 解决方法

    Ubuntu14.04 panic --not syncing: Attempt to kill init 解决方法 工作电脑装了一个虚拟机玩玩,胡乱下载了一些软件,apt-get了不少操作,后来重启 ...

  6. 【转】unity 热更新思路和实现

    声明:本文介绍的热更新方案是我在网上搜索到的,然后自己修改了一下,相当于是借鉴了别人的思路,加工成了自己的,在此感谢无私分享经验的朋友们. 想要使用热更新技术,需要规划设计好资源比较策略,资源版本,确 ...

  7. netty in action 笔记 二

    netty的数据容器 网络数据的基本单位大多为字节,Java NIO 提供了ByteBuffer 作为它的字节容器,但使用起来过于复杂和繁琐.在Netty中, ByteBuffer 替代品是ByteB ...

  8. 关于mysql开元数据库的几个随想

    现在已经是凌晨了,昨天晚上写了我人生中的第一篇笔记,觉得没什么可写的,写了一个多小时都没写出什么,现在突然想写点东西了,这是一个比较有趣的问题,前两个月换了新工作,记得当初面试这份工作的时候面试到第三 ...

  9. py3.6+anaconda下安装opencv3

    py3.6+anaconda下安装opencv3 首先声明-网上的方法大多数都是有毒的.也不知道给的什么鬼方法都不行. 我说下我的方法.去这个网站https://pypi.tuna.tsinghua. ...

  10. Ubuntu使用时遇到的问题

    启动时显示System program problem detected 解决办法: 打开命令行窗口:Ctrl+Alt+T 执行命令:sudo gedit /etc/default/apport 把e ...