[HEOI2013]SAO(树上dp,计数)
[HEOI2013]SAO
(这写了一个晚上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\)个空位里,那么状态转移方程就是:
\]
如果是v先于u再像上面那样分析就可以了.
#include<bits/stdc++.h>
#define maxn 1005
#define mod 1000000007
#define ll long long
using 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<m)return 0;return c[n][m];}
void qm(int &x,int y){x+=y;if(x>=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<n;i++)//u优先v,连1边
cin>>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;
}
[HEOI2013]SAO(树上dp,计数)的更多相关文章
- 3167: [Heoi2013]Sao [树形DP]
3167: [Heoi2013]Sao 题意: n个点的"有向"树,求拓扑排序方案数 Welcome to Sword Art Online!!! 一开始想错了...没有考虑一个点 ...
- [BZOJ3167][P4099][HEOI2013]SAO(树形DP)
题目描述 Welcome to SAO ( Strange and Abnormal Online).这是一个 VR MMORPG, 含有 n 个关卡.但是,挑战不同关卡的顺序是一个很大的问题. 有 ...
- BZOJ 3167 [Heoi2013]Sao ——树形DP
BZOJ4824的强化版. 改变枚举的方案,使用前缀和进行DP优化. 然后复杂度就是$O(n^2)$了. #include <map> #include <cmath> #in ...
- [BZOJ3167][HEOI2013]SAO[树dp+组合数学]
题意 给定 \(n\) 个节点和 \(n-1\) 个限制,每个节点有一个权值,每个限制形如:\(a_i< a_j\) ,问有多少个 \(1\) 到 \(n\) 排列满足要求. \(n\leq 1 ...
- 洛谷 4099 [HEOI2013]SAO——树形DP
题目:https://www.luogu.org/problemnew/show/P4099 结果还是看了题解才会…… 关键是状态,f[ i ][ j ] 表示 i 子树. i 号点是第 j 个出现的 ...
- P4099 [HEOI2013]SAO(树形dp)
P4099 [HEOI2013]SAO 我们设$f[u][k]$表示以拓扑序编号为$k$的点$u$,以$u$为根的子树中的元素所组成的序列方案数 蓝后我们在找一个以$v$为根的子树. 我们的任务就是在 ...
- 【BZOJ3167】[HEOI2013]SAO(动态规划)
[BZOJ3167][HEOI2013]SAO(动态规划) 题面 BZOJ 洛谷 题解 显然限制条件是一个\(DAG\)(不考虑边的方向的话就是一棵树了). 那么考虑树型\(dp\),设\(f[i][ ...
- P4099 [HEOI2013]SAO
P4099 [HEOI2013]SAO 贼板子有意思的一个题---我()竟然没看题解 有一张连成树的有向图,球拓扑序数量. 树形dp,设\(f[i][j]\)表示\(i\)在子树中\(i\)拓扑序上排 ...
- BZOJ 3167: [Heoi2013]Sao
3167: [Heoi2013]Sao Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 96 Solved: 36[Submit][Status][D ...
随机推荐
- 从无到有构建vue实战项目(六)
十.徒手撸一个vue下拉左侧二级导航 先附上最终效果图: vue代码: <div class="dropdown-menu-explore" v-on:mouseover=& ...
- 洛谷 P1101-题解
这道题可以用深搜(回溯)来写,相信大部分人都是这么想的,但是有些人可能在一些地方饶了半天,所以这里就贴一下我的思路,个人觉得自己的很好懂,除了tx和ty那里,但是tx和ty的那种用法对于输出路径的题目 ...
- C#7.0 新增功能
连载目录 [已更新最新开发文章,点击查看详细] C# 7.0 向 C# 语言添加了许多新功能 01 out 变量 支持 out 参数的现有语法已在此版本中得到改进. 现在可以在方法调用的参数列表 ...
- python+selenium实现163邮箱登陆—iframe动态ID定位 及常用定位方法
今天发现之前的登录163邮箱脚本定位不到iframe了,原因是iframe拼接了动态ID,修改后的脚本如下: from selenium import webdriver driver = webdr ...
- Chrome离线安装包+谷歌访问助手
Chrome离线安装包+谷歌访问助手 所有chrome版本离线安装包下载地址 谷歌访问助手v2.3.0.crx(需要修改主页) 谷歌上网助手v1.4.3.crx(不用修改主页,需要注册) 两个插件为2 ...
- 基于Spark Grahpx+Neo4j 实现用户社群发现
上一篇文章知识图谱在大数据中的应用我们介绍了知识图谱的一些概念和应用场景,今天我们就来看一个具体的应用案例了解下知识图谱的应用.用户增长对于一个APP的生存起到了至关重要的作用,没有持续的用户增长,再 ...
- 100天搞定机器学习|Day13-14 SVM的实现
昨天我们学习了支持向量机基本概念,重申数学推导原理的重要性并向大家介绍了一篇非常不错的文章.今天,我们使用Scikit-Learn中的SVC分类器实现SVM.我们将在day16使用kernel-tri ...
- IO流的Properties集合,序列化流与反序列化流,打印流及commons-IO
内容介绍 Properties集合 序列化流与反序列化流 打印流 commons-IO Properties类 Properties类介绍 Properties 类表示了一个持久的属性集.Proper ...
- 林大妈的JavaScript基础知识(三):JavaScript编程(4)数组
数组,是一段线性分配的,具有非常高性能的数据结构.简单地说,数组以连续的空间存储,通过整数地计算偏移量访问其中的元素,将读取修改的时间复杂度降低至O(1),我们称之为猝发式存取.是不是非常期待?没错, ...
- 设置Myeclipse的jvm内存参数
Myeclipse经常会遇到内存溢出和Gc开销过大的情况,这时候就需要修改Myeclipse的Jvm内存参数 修改如下:(使用Extjs做公司大项目时候,不要让项目Builders的Javascrip ...