熟练剖分(tree) 树形DP

题目描述

题目传送门

分析

我们设\(f[i][j]\)为以\(i\)为根节点的子树中最坏时间复杂度小于等于\(j\)的概率

设\(g[i][j]\)为当前扫到的以\(i\)为父亲节点的所有儿子最坏时间复杂度小于等于\(j\)的概率之和

因为每遍历到一个新的节点,原来的\(g\)数组中的值就要全部更新,因此我们压掉第一维

下面我们考虑转移

对于当前枚举到的某一个节点,我们用三重循环分别扫一边

第一重循环代表当前哪一个节点充当重儿子,第二重循环枚举所有儿子,第三充循环枚举最坏时间复杂度\(k\)

如果第二重循环中枚举的儿子恰好是重儿子的话,那么父亲节点的最坏时间复杂度为\(k\)的情况可以由两种情况转移过来

第一种情况就是重儿子的时间复杂度恰好为\(k\)的概率乘上其它儿子时间复杂度小于等于\(k\)的概率

第二种情况就是其它儿子的时间复杂度恰好为\(k\)的概率乘上重儿子的时间复杂度小于等于\(k\)的概率

不要忘了减去重复的情况

如果第二重循环中枚举的儿子不是重儿子的话,那么父亲节点的最坏时间复杂度为\(k\)的情况可以由两种情况转移过来

第一种情况就是重儿子的时间复杂度恰好为\(k-1\)的概率乘上其它儿子时间复杂度小于等于\(k\)的概率

第二种情况就是其它儿子的时间复杂度恰好为\(k\)的概率乘上重儿子的时间复杂度小于等于\(k-1\)的概率

也不要忘了减去重复的情况

代码

#include<cstdio>
#include<cstring>
#include<vector>
inline int read(){
int x=0,fh=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=3e3+5;
const int mod=1e9+7;
int fa[maxn],head[maxn],tot=1,n,rt;
struct asd{
int to,next;
}b[maxn<<1];
void ad(int aa,int bb){
b[tot].to=bb;
b[tot].next=head[aa];
head[aa]=tot++;
}
int ksm(int ds,int zs){
int ans=1;
while(zs){
if(zs&1) ans=1LL*ans*ds%mod;
ds=1LL*ds*ds%mod;
zs>>=1;
}
return ans;
}
int son[maxn],siz[maxn];
long long f[maxn][maxn],g[maxn],h[maxn];
void dfs(int now){
siz[now]=1;
for(int i=head[now];i!=-1;i=b[i].next){
int u=b[i].to;
if(u==fa[now]) continue;
dfs(u);
siz[now]+=siz[u];
}
int p=ksm(son[now],mod-2);
for(int i=head[now];i!=-1;i=b[i].next){
if(b[i].to==fa[now]) continue;
for(int j=0;j<=n;j++) g[j]=1;
//初始化g数组
int zez=b[i].to;
//枚举重儿子
for(int j=head[now];j!=-1;j=b[j].next){
if(b[j].to==fa[now]) continue;
int qez=b[j].to;
//枚举其它儿子
for(int k=0;k<=siz[qez]+1;k++){
//枚举最大时间复杂度
long long qt=g[k],xz=f[qez][k];
if(k) qt-=g[k-1],xz-=f[qez][k-1];
if(zez==qez){
h[k]=(qt*f[qez][k]%mod+xz*g[k]%mod-xz*qt%mod+mod)%mod;
} else {
xz=f[qez][k-1];
if(k>1) xz-=f[qez][k-2];
h[k]=(qt*f[qez][k-1]%mod+xz*g[k]%mod-xz*qt%mod+mod)%mod;
}
}
g[0]=h[0],h[0]=0;
for(int k=1;k<=siz[qez]+1;k++){
g[k]=(g[k-1]+h[k])%mod;
h[k]=0;
}
//h数组临时存储状态
}
for(int j=siz[now];j>=1;j--){
g[j]=(g[j]-g[j-1]+mod)%mod;
//将前缀和数组还原成正常数组
}
for(int j=0;j<=siz[now];j++){
f[now][j]=(f[now][j]+g[j]*p%mod)%mod;
}
}
if(son[now]==0) f[now][0]=1;
for(int i=1;i<=siz[now]+1;i++){
f[now][i]=(f[now][i]+f[now][i-1])%mod;
}
}
int main(){
memset(head,-1,sizeof(head));
n=read();
int aa;
for(int i=1;i<=n;i++){
son[i]=read();
for(int j=1;j<=son[i];j++){
aa=read(),fa[aa]=i;
ad(i,aa),ad(aa,i);
}
}
rt=1;
while(fa[rt]) rt=fa[rt];
dfs(rt);
long long ans=0;
for(int i=1;i<=n;i++){
ans=(ans+i*(f[rt][i]-f[rt][i-1]+mod)%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}

熟练剖分(tree) 树形DP的更多相关文章

  1. 【模拟7.14】B. 熟练剖分(tree) (概率DP)

    一道概率神题,考试时没读清题考完看了学长的玄学题解看了好几个小时 首先f[i][j]表示在点 i 为根的子树中,向下最长轻链长度小于等于 j 的概率. 首先递归下去并求出子树大小,然后枚举重儿子,枚举 ...

  2. 20210501 序列,熟练剖分(tree),建造游乐园(play)

    考场 \(65+5+0\),并列 rk2 最高分 \(55+10+10\) T1:等比数列可以写作 \(q^kx\),发现 \(q\le1000\) 且有一档分为 \(a_i\le100\),想到 \ ...

  3. 长链剖分优化树形DP总结

    长链剖分 规定若\(x\)为叶结点,则\(len[x]=1\). 否则定义\(preferredchild[x]\)(以下简称\(pc[x]\),称\(pc[x]\)为\(x\)的长儿子)为\(x\) ...

  4. hdu-5834 Magic boy Bi Luo with his excited tree(树形dp)

    题目链接: Magic boy Bi Luo with his excited tree Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: ...

  5. CF 461B Appleman and Tree 树形DP

    Appleman has a tree with n vertices. Some of the vertices (at least one) are colored black and other ...

  6. codeforces 161D Distance in Tree 树形dp

    题目链接: http://codeforces.com/contest/161/problem/D D. Distance in Tree time limit per test 3 secondsm ...

  7. hdu6035 Colorful Tree 树形dp 给定一棵树,每个节点有一个颜色值。定义每条路径的值为经过的节点的不同颜色数。求所有路径的值和。

    /** 题目:hdu6035 Colorful Tree 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6035 题意:给定一棵树,每个节点有一个颜色值.定 ...

  8. 5.10 省选模拟赛 tree 树形dp 逆元

    LINK:tree 整场比赛看起来最不可做 确是最简单的题目. 感觉很难写 不过单独考虑某个点 容易想到树形dp的状态. 设f[x]表示以x为根的子树内有黑边的方案数. 白边方案只有一种所以不用记录. ...

  9. Codeforces Round #263 Div.1 B Appleman and Tree --树形DP【转】

    题意:给了一棵树以及每个节点的颜色,1代表黑,0代表白,求将这棵树拆成k棵树,使得每棵树恰好有一个黑色节点的方法数 解法:树形DP问题.定义: dp[u][0]表示以u为根的子树对父亲的贡献为0 dp ...

随机推荐

  1. Canal v1.1.4版本避坑指南

    前提 在忍耐了很久之后,忍不住爆发了,在掘金发了条沸点(下班时发的): 这是一个令人悲伤的故事,这条情感爆发的沸点好像被屏蔽了,另外小水渠(Canal意为水道.管道)上线一段时间,不出坑的时候风平浪静 ...

  2. 从零搭建Spring Boot脚手架(4):手写Mybatis通用Mapper

    1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文把国内最流行的ORM框架Mybatis也集成了进去.但是很多时候我们希望有一些开箱即用的通用Mapper来简化我们的开发.我 ...

  3. springboot中使用定时器

    springboot使用注解注册定时器 @Configuration @EnableScheduling public class WeatherDataTask implements Schedul ...

  4. 用python爬虫监控CSDN博客阅读量

    作为一个博客新人,对自己博客的访问量也是很在意的,刚好在学python爬虫,所以正好利用一下,写一个python程序来监控博客文章访问量 效果 代码会自动爬取文章列表,并且获取标题和访问量,写入exc ...

  5. 树莓派搭建网站wordpress的url写错 问题解决方法 有效GUI方法

    这个时候wordpress的后台已经登陆不了了,所以要对数据库做一些改变. 先说一下我是跟b站韩博士学的,LNMP.如果我们用的不一个方法的话下面就不用看了.下面是具体方法: 1  浏览器登录phpm ...

  6. antd-vue的select组件实现既可以输入添加,又可以下拉选择

    最近,项目中碰到需求,要求任务类型可以从下拉框(后台返回的数据)中选择,也可以手动输入添加新项,项目用的是antd-vue,所以最接近的组件就是a-select组件了,废话不多说,改造方法如下: HT ...

  7. Java多线程_缓存对齐

    1.什么是缓存对齐 当前的电脑中,数据存储在磁盘上,可以断电保存,但是读取效率较低.不断电的情况下,数据可以在内存中存储,相对硬盘效率差不多是磁盘的一万倍左右.但是运算时,速度最快的是直接缓存在CPU ...

  8. 喵的Unity游戏开发之路 - 推球:游戏中的物理

    很多童鞋没有系统的Unity3D游戏开发基础,也不知道从何开始学.为此我们精选了一套国外优秀的Unity3D游戏开发教程,翻译整理后放送给大家,教您从零开始一步一步掌握Unity3D游戏开发. 本文不 ...

  9. 七脚OLED屏幕使用IIC接口

    7pin 0.96寸OLED模块支持SPI和IIC接口 默认是SPI接口;如果想用IC接口;操作如下几步骤: 1.将模块背面的电阻R3换到R1位置,此时将模块切换为IIC接口:电阻R8可以用0欧姆电阻 ...

  10. TCL(事务控制语言)

    #TCL/*Transaction Control Language 事务控制语言 事务:一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行. 案例:转账 张三丰 1000 ...