博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[HEOI2013]SAO(树上dp,计数)
阅读量:5101 次
发布时间:2019-06-13

本文共 3319 字,大约阅读时间需要 11 分钟。

(这写了一个晚上QAQ,可能是我太蠢了吧.)

题目说只有\(n-1\)条边,然而每个点又相互联系.说明它的结构是一个类似树的结构,但是是有向边连接的,题目问的是方案个数,那么首先想到的肯定是树上dp.

但是这题有向边,从一个点出发,不一定可以遍历整棵树.那么肯定要对每条边建反边,打个标记分类讨论.
解决了建模的问题,接下来就是怎么dp了.
容易发现这个其实就是求这个"类树形图"的拓扑排序方案数,然而我们知道拓扑排序的方案计数是个NP问题,用状压解决,这题有"类树"这个优良特性.所以我们要另辟蹊径.
我们设状态的时候要保证这个状态尽量可以描述这个状态的特点,从而协助我们转移.考虑状态\(dp[u][i]\)表示u和它的子树的点组成的满足拓扑关系的排列的中u点在第i位上.这样以来,我们就根据这个状态知道了,u的位置,知道了u之前有多少个点,知道u之后有多少个点,我们还可以通过它和它子树的节点数推断要在一些范围插入一些点,是不是很棒~
考虑怎么转移呢?
首先是边界条件,一个点(没有子树)单独站在第一位的方案数是1.
再就是考虑合并子树信息.先讨论u优先于v的情况.
最开始,只有1个点,此时加入第一棵子树,这个时候\(dp[u][i]\)描述的状态到处都是空位,出了u站在i位置,其他的都随便这棵子树里的点乱站.我们考虑从\(dp[v][j]\)转移而来,于是就是u和v的位置固定了,其他的可以随意填充,其他的点有多少个呢?\(sz[v]\)吗?
等等好像还有一些限制! u要先于v! 那么v在合并后的排名必须在u之后,那么v在之前子树的排名最低最低也是\(k-i+1\)名,这样v就刚好在u后面一位.那么其他的\(k-1\)个点就随意分布u之前的\(i-1\)个空位之间,剩余的\(sz[u]-k\)个点在\(u\)之后的\(sz[u]-sz[v]-k\)个空位里,那么状态转移方程就是:
\[dp[u][i]=\sum_{k=1}^{min(i,sz[u]-sz[v]+1)}dp[u][k]*C_{sz[u]-sz[v]-k}^{sz[u]-k}*C_{i-1}^{k-1}\sum_{j=k-u+1}^{sz[v]}dp[v][j]\]
如果是v先于u再像上面那样分析就可以了.

#include
#define maxn 1005#define mod 1000000007#define ll long longusing namespace std;int cnt,n,T,ans,sum[maxn][maxn];//dp[u][i]表示u节点和它的子树,组成的拓扑排列中,u的拓扑序int c[maxn][maxn],dp[maxn][maxn],sz[maxn],vis[maxn];int head[maxn],nxt[maxn<<1],w[maxn<<1],to[maxn<<1];int C(int n,int m){if(n<0||m<0||n
=mod)x-=mod;}void add(int u,int v,int ww){ nxt[++cnt]=head[u];head[u]=cnt; to[cnt]=v;w[cnt]=ww;}void init(){ for(int i=0;i<=1000;i++)c[i][0]=1; for(int i=1;i<=1000;i++) for(int j=1;j<=i;j++)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;}void clear(){ memset(dp,0,sizeof(dp));memset(sz,0,sizeof(sz)); memset(vis,0,sizeof(vis));memset(sum,0,sizeof(sum)); memset(head,0,sizeof(head));cnt=0;}void dfs(int u){ vis[u]=1;sz[u]=1;dp[u][1]=1; for(int ii=head[u];ii;ii=nxt[ii]) { int v=to[ii];if(vis[v])continue; dfs(v);sz[u]+=sz[v]; if(w[ii])//u优先于v { for(int i=sz[u],s=0;i>=1;i--,dp[u][i+1]=s,s=0)//填表法合并子树信息,枚举u的拓扑序 for(int k=1;k<=min(sz[u]-sz[v],i);k++)//枚举u和其它已经合并到u上的子树的状态 { if(sz[v]<=i-k)continue; int tt=(sum[v][sz[v]]-sum[v][i-k]+mod)%mod; qm(s,(ll)dp[u][k]*tt%mod*C(i-1,k-1)%mod *C(sz[u]-i,sz[u]-sz[v]-k)%mod); /* 暴力转移: for(int j=i-k+1;j<=sz[v];j++) qm(sum,(ll)dp[u][k]*dp[v][j]%mod *C(i-1,k-1)%mod*C(sz[u]-i,sz[u]-sz[v]-k)%mod); */ } } else//v优先于u { for(int i=sz[u],s=0;i>=1;i--,dp[u][i+1]=s,s=0) for(int k=1;k<=min(sz[u]-sz[v],i-1);k++) { int tt=sum[v][min(sz[v],i-k)]; qm(s,(ll)dp[u][k]*tt%mod*C(i-1,k-1)%mod *C(sz[u]-i,sz[u]-sz[v]-k)%mod); } } } for(int i=1;i<=sz[u];i++)//前缀和优化 sum[u][i]=(sum[u][i-1]+dp[u][i])%mod;}int main(){ init();cin>>T; while(T--) { char ty;clear();cin>>n; for(int i=1,u,v;i
>u>>ty>>v,v++,u++,add(u,v,ty=='<'),add(v,u,ty=='>'); dfs(1);ans=0; for(int i=1;i<=n;i++)ans+=dp[1][i],ans%=mod; printf("%d\n",ans); } return 0;}

转载于:https://www.cnblogs.com/terribleterrible/p/9844044.html

你可能感兴趣的文章
WPF中实现多选ComboBox控件
查看>>
读构建之法第四章第十七章有感
查看>>
Windows Phone开发(4):框架和页 转:http://blog.csdn.net/tcjiaan/article/details/7263146
查看>>
python asyncio 异步实现mongodb数据转xls文件
查看>>
TestNG入门
查看>>
【ul开发攻略】HTML5/CSS3菜单代码 阴影+发光+圆角
查看>>
IOS-图片操作集合
查看>>
IO—》Properties类&序列化流与反序列化流
查看>>
Codeforces 719B Anatoly and Cockroaches
查看>>
ActiveMQ与spring整合
查看>>
EOS生产区块:解析插件producer_plugin
查看>>
格式化输出数字和时间
查看>>
关于TFS2010使用常见问题
查看>>
URL编码与解码
查看>>
Eclipse 安装SVN插件
查看>>
阿里云服务器CentOS6.9安装Mysql
查看>>
剑指offer系列6:数值的整数次方
查看>>
js 过滤敏感词
查看>>
poj2752 Seek the Name, Seek the Fame
查看>>
软件开发和软件测试,我该如何选择?(蜗牛学院)
查看>>