[***]HZOI20190714 T2熟练剖分
这题真的神仙,蒟弱表示看题解看不懂……std看了几个小时大概看懂,还有一些细节的东西没有思考。
最难受的是题解和std好像并不是同一个人写的……数组状态不一样……看了好久才看出来f也是前缀和。
F[i][j]表示在点 i 为根的子树中,向下最长轻链长度小于等于 j 的概率。
首先递归下去并求出子树大小,然后枚举重儿子,枚举该点最长轻链长度,再次枚举儿子节点并逐个考虑(感觉和概率充电器(特地从考试总结中搬出来了)那个题有点类似),
假设当前枚举的重儿子是v(i),枚举到儿子节点v(j),x最长轻链长度为k,设gs为v(j)之前考虑的儿子中最长轻链长度为k的概率(因为是前缀和,所以代码中有减这个操作,f同理),如果v(j)=v(i)即v(j)为重儿子,则设fs为以v(j)为根的子树最长轻链长度为k的概率,f[x][k]=gs*f[v(j)][k](v(j)之前考虑的儿子为长度k*以v(j)为根字数长度<=k(此条边为重链所以可以等于))+fs*g[k]-gs*fs(去重),
如果v(j)是轻儿子,则设fs为以v(j)为根的子树最长轻链长度为k-1的概率,f[x][k]=gs*f[v(j)][k-1]+fs*g[k]-gs*fs,大致同上,只是x与v(j)相连的这条边为轻链所以有减1,值得提醒的一点是这里的f[x][k]并不是最终的f[x][k],只是考虑到当前几个儿子时的值,一个儿子一个儿子地向里加。考虑到f数组直接改的话会错,所以用h数组保存,最后加到g数组中清空h,当v(i)为重儿子这个情况考虑玩后将g数组加到f中去,清空g。当前节点x求完后,此时的f数组并不是前缀和,所以需要再次转化。
最后求答案时再次将前缀和转化为单个的值(换来换去好乱啊……)。
还有一点,时间复杂度不容乐观啊,转移的时候如果k循环到了 size[x],那么复杂度可以被卡到 N^3,我们发现当 k>size[v(j)]+1 的时候f[i]没有变,所以只要 k 循环到 size[v(j)]+1 就行了。
每个节点只有在 dp 它父亲时会被枚举成为重儿子,然后最多把整棵树的大小扫一遍,所以复杂度为$n^2$(蒟弱表示并不会证).
ps.有同学问我g数组的含义(好像是没写太清楚),g[k]=∑f[x][l](l<=k)即前缀和,可能用文字描述更好理解点,就是v(j)之前的儿子中最长轻链长度小于等于k的概率。
1 LL f[3010][3010],g[3010],h[3010],size[3010]; 2 void dfs(int x) 3 { 4 size[x]=1; 5 for(int i=f(x);i;i=n(i))dfs(v(i)),size[x]+=size[v(i)]; 6 LL q=poww(du[x],mod-2); 7 for(int i=f(x);i;i=n(i))//枚举重儿子 8 { 9 for(int j=0;j<=n;j++)g[j]=1; 10 for(int j=f(x);j;j=n(j)) 11 { 12 for(int k=0;k<=size[v(j)]+1;k++) 13 { 14 LL gs=g[k]; if(k)gs-=g[k-1]; 15 LL fs=f[v(j)][k];if(k)fs-=f[v(j)][k-1]; 16 if(v(j)==v(i))h[k]= ((gs*f[v(j)][k]%mod+fs*g[k]%mod-gs*fs)%mod+mod)%mod; 17 else if(k) 18 { 19 fs=f[v(j)][k-1];if(k>1)fs-=f[v(j)][k-2]; 20 h[k]=( (gs*f[v(j)][k-1]%mod+fs*g[k]%mod-fs*gs%mod)%mod+mod )%mod; 21 } 22 } 23 g[0]=h[0],h[0]=0; 24 for(int k=1;k<=size[v(j)]+1;k++)g[k]=(g[k-1]+h[k])%mod,h[k]=0; 25 } 26 for(int j=size[x];j;j--)g[j]=(g[j]-g[j-1]+mod)%mod; 27 for(int j=0;j<=size[x];j++)f[x][j]=(f[x][j]+g[j]*q%mod)%mod; 28 } 29 if(!f(x))f[x][0]=1; 30 for(int i=1;i<=n;i++)f[x][i]=(f[x][i-1]+f[x][i])%mod; 31 }
#include<iostream>
#include<cstdio>
#include<cmath>
#define LL long long
#define int LL
#define MAXN 50100
#define esp 1e-8
#define mod 1000000007
using namespace std;
struct edge
{int u,v,nxt;#define u(x) ed[x].u#define v(x) ed[x].v#define n(x) ed[x].nxt
}ed[5000000];
int first[MAXN],num_e;
#define f(x) first[x]
int n,root;
int du[MAXN],ru[MAXN];LL poww(LL a,int b);
inline void add(int u,int v)
{++num_e;u(num_e)=u;v(num_e)=v;n(num_e)=f(u);f(u)=num_e;
}
inline int read()
{int s=0;char a=getchar();while(a<'0'||a>'9')a=getchar();while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();}return s;
}
LL f[3010][3010],g[3010],h[3010],size[3010];void dfs(int x)
{size[x]=1;for(int i=f(x);i;i=n(i))dfs(v(i)),size[x]+=size[v(i)];
// cout<<x<<" "<<size[x]<<endl;LL q=poww(du[x],mod-2);for(int i=f(x);i;i=n(i))//枚举重儿子{ for(int j=0;j<=n;j++)g[j]=1;for(int j=f(x);j;j=n(j)){for(int k=0;k<=size[v(j)]+1;k++){LL gs=g[k]; if(k)gs-=g[k-1];LL fs=f[v(j)][k];if(k)fs-=f[v(j)][k-1];if(v(j)==v(i))h[k]= ((gs*f[v(j)][k]%mod+fs*g[k]%mod-gs*fs)%mod+mod)%mod;else if(k){fs=f[v(j)][k-1];if(k>1)fs-=f[v(j)][k-2];h[k]=( (gs*f[v(j)][k-1]%mod+fs*g[k]%mod-fs*gs%mod)%mod+mod )%mod;}}g[0]=h[0],h[0]=0;for(int k=1;k<=size[v(j)]+1;k++)g[k]=(g[k-1]+h[k])%mod,h[k]=0;}for(int j=size[x];j;j--)g[j]=(g[j]-g[j-1]+mod)%mod;for(int j=0;j<=size[x];j++)f[x][j]=(f[x][j]+g[j]*q%mod)%mod;}if(!f(x))f[x][0]=1;for(int i=1;i<=n;i++)f[x][i]=(f[x][i-1]+f[x][i])%mod;
}
signed main()
{
// freopen("in.txt","r",stdin);n=read();int a;
// for(int i=0;i<=n;i++)m[i]=1;for(int i=1;i<=n;i++) {du[i]=read();for(int j=1;j<=du[i];j++)a=read(),add(i,a),ru[a]++;}bool pdl=1;for(int i=1;i<=n;i++){if(du[i]!=1&&du[i]!=0)pdl=0;if(!ru[i])root=i;}dfs(root);LL ans=0;for(int i=1;i<=n;i++)ans=(ans+i*(f[root][i]-f[root][i-1]+mod)%mod)%mod;printf("%lld\n",ans);
}
LL poww(LL a,int b)
{LL ans=1;while(b){if(b&1)ans=(ans*a)%mod;a=(a*a)%mod;b=b>>1;}return ans;
}
转载于:https://www.cnblogs.com/Al-Ca/p/11188037.html
[***]HZOI20190714 T2熟练剖分相关推荐
- B. 熟练剖分(tree) (概率DP)
一道概率神题,考试时没读清题考完看了学长的玄学题解看了好几个小时 首先f[i][j]表示在点 i 为根的子树中,向下最长轻链长度小于等于 j 的概率. 首先递归下去并求出子树大小,然后枚举重儿子,枚举 ...
- Housewife Wind POJ - 2763 倍增LCA+树状数组 或 树链剖分+线段树
题目 链接:http://poj.org/problem?id=2763 Language:Default Housewife Wind Time Limit: 4000MS Memory Lim ...
- 小清的树链剖分10题日志01 树链剖分种果子 有你好果子吃的
声明:由于本人能力尚不优 故无法做出解释文章 此文仅为自己日记 感谢你的阅读 作为队里的数据结构选手 2019西安邀请赛的E题 竟然在有机时的情况下 想到了线段树log^2的拆位做法 但是题目路径把自 ...
- OI Journal
佛系更新,哪天想起来就写点. 10.11 班主任硬灌的鸡汤真香 qtmd,简直就是硬扯,说怎么怎么着说不定就能多拿一分两分,一分两分就能割掉好多人 ......螺旋懵圈状,我表示硬憋着不笑 HIAHI ...
- BZOJ 4034 树上操作
熟练剖分 不想写树剖怎么办QwQ 维护每个点\(Ans\) 操作\(1\)就是子树加 操作\(2\)是操作\(1\)的叠加...... 认真分析...... 子树加\(dep\)!!! 写个树状数组节 ...
- codeforces 549F Yura and Developers(分治、启发式合并)
codeforces 549F Yura and Developers 题意 给定一个数组,问有多少区间满足:去掉最大值之后,和是k的倍数. 题解 分治,对于一个区间,找出最大值之后,分成两个区间. ...
- 线段树合集——杨子曰算法
线段树合集--杨子曰算法 这里我把我写的五篇线段树汇总一下: 线段树(一):主要讲了线段树是什么鬼,以及怎样query(←想知道它是什么meaning,点进去!) 线段树(二):体现了线段树真正的价值 ...
- BZOJ 4034: [HAOI2015]T2 树链剖分
4034: [HAOI2015]T2 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操 ...
- 【BZOJ4034】T2,树链剖分练习
传送门 写在前面:大晚上写题解,明天上午去看电影,可以的~~ 思路:比较裸的树链剖分,比起普通的链剖,这里加了一步对子树(包括自己)的权值修改,这其实很简单,因为我们dfs时任一个子树的区间[L,R] ...
最新文章
- 对RTMP视频流进行BitmapData.draw()出错的解决办法
- ElasticSearch技术文档
- 飞鸽传书(IPMSG)协议(翻译稿)
- 滑块 组件_组件制作:如何使用链接的输入创建滑块
- Delphi常用字符串函数
- IntelliJ IDEA 12.0.3 更新版发布
- 小学计算机教案 插入艺术字,小学信息技术《在幻灯片中插入艺术字》说课及反思...
- 39节点含太阳能算例
- OFDM-训练序列与导频
- pb语言是什么计算机语言,pb编程语言排行榜_世界编程语言排行榜 搜狗百科
- Flutter Clipboard 粘贴板使用
- Going Deeper with Convolutions——GoogLeNet论文翻译——中文版
- 什么是ARM TCM内存
- 输入三个整数a,b,c。并进行两两相加,最后比较相加和的最大值。
- html在360浏览器如何修改字体大小,360浏览器网页中字体大小如何修改
- win高分屏下Qt程序的界面适配处理
- 从360大战QQ看未来云计算和云存储
- SAP FICO F-32和F-44设置默认凭证类型
- 辐射度算法(radiosity)原理
- 基于matlab的现代通信原理应用
热门文章
- python3打包成exe运行 无法定位程度输入点_Pyinstaller递归错误解决方案
- [HNOI2007] 分裂游戏
- [GCJ] Qualification Round 2017
- Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序
- 关于cookie使用的几个方法
- Applying a Color Wash to Images
- php image处理,php 中图像压缩处理类(二)imageutil.php
- DMA流程简介--CPU/内存/网卡之间的交互
- List大坑集「锦」
- Hive文件数创建过多的问题