感觉是一道很妙的树形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. 抓取Oracle数据快照

    进入到oracle安装目录下的admin(找到这个目录)开启cmd键入sqlplus system/mima@实例名>@awrrpt.sql Would you like an HTML rep ...

  2. Git笔记——01

    Git - 幕布 Git   教程:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b00 ...

  3. MySQL☞sign函数

    sign( )函数:判断数值的正负性,如果数值是正数,返回值是1,如果该数值是负数,返回值是-1,如果该数值是 0,返回值也是0. 格式: select sign(数值) from 表名 例子: 1. ...

  4. python常用函数—enumerate()

    enumerate() 对于一个可迭代的(iterable)/可遍历的对象(如列表.字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值的元组. 使用拆包,可以单独获得索引和值 ...

  5. 1.编译azkaban

    1.下载azkaban的源码 https://github.com/azkaban/azkaban.git 然后解压得到azkaban-master.zip,解压:unzip azkaban-mast ...

  6. Python3 数据类型-字符串

    字符串是 Python 中最常用的数据类型,是一个个字符组成的有序的序列,是字符的集合. 一 字符串定义 创建字符串很简单,可以使用引号('或"或""")来创建 ...

  7. LVS+Keepalive+Nginx实现负载均衡

    本文参考:http://blog.csdn.net/yinwenjie/article/details/47211551 简单粗暴写一下,做备忘,刚刚搭好没做优化呢,后期补充 一.机器准备 LVS-M ...

  8. 浅谈蓝牙低功耗(BLE)的几种常见的应用场景及架构(转载)

    转载来至beautifulzzzz,网址http://www.cnblogs.com/zjutlitao/,推荐学习 蓝牙在短距离无线通信领域占据举足轻重的地位—— 从手机.平板.PC到车载设备, 到 ...

  9. Thunder团队第六周 - Scrum会议3

    Scrum会议3 小组名称:Thunder 项目名称:i阅app Scrum Master:李传康 工作照片: 参会成员: 王航:http://www.cnblogs.com/wangh013/ 李传 ...

  10. 第十六次ScrumMeeting会议

    第十六次Scrum Meeting 时间:2017/12/6 地点:线上+SPR咖啡馆 人员:蔡帜 王子铭 游心 解小锐 王辰昱 李金奇 杨森 陈鑫 照片: 目前工作进展 名字 今日 明天的工作 遇到 ...