[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\)个空位里,那么状态转移方程就是:

\[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<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,计数)的更多相关文章

  1. 3167: [Heoi2013]Sao [树形DP]

    3167: [Heoi2013]Sao 题意: n个点的"有向"树,求拓扑排序方案数 Welcome to Sword Art Online!!! 一开始想错了...没有考虑一个点 ...

  2. [BZOJ3167][P4099][HEOI2013]SAO(树形DP)

    题目描述 Welcome to SAO ( Strange and Abnormal Online).这是一个 VR MMORPG, 含有 n 个关卡.但是,挑战不同关卡的顺序是一个很大的问题. 有 ...

  3. BZOJ 3167 [Heoi2013]Sao ——树形DP

    BZOJ4824的强化版. 改变枚举的方案,使用前缀和进行DP优化. 然后复杂度就是$O(n^2)$了. #include <map> #include <cmath> #in ...

  4. [BZOJ3167][HEOI2013]SAO[树dp+组合数学]

    题意 给定 \(n\) 个节点和 \(n-1\) 个限制,每个节点有一个权值,每个限制形如:\(a_i< a_j\) ,问有多少个 \(1\) 到 \(n\) 排列满足要求. \(n\leq 1 ...

  5. 洛谷 4099 [HEOI2013]SAO——树形DP

    题目:https://www.luogu.org/problemnew/show/P4099 结果还是看了题解才会…… 关键是状态,f[ i ][ j ] 表示 i 子树. i 号点是第 j 个出现的 ...

  6. P4099 [HEOI2013]SAO(树形dp)

    P4099 [HEOI2013]SAO 我们设$f[u][k]$表示以拓扑序编号为$k$的点$u$,以$u$为根的子树中的元素所组成的序列方案数 蓝后我们在找一个以$v$为根的子树. 我们的任务就是在 ...

  7. 【BZOJ3167】[HEOI2013]SAO(动态规划)

    [BZOJ3167][HEOI2013]SAO(动态规划) 题面 BZOJ 洛谷 题解 显然限制条件是一个\(DAG\)(不考虑边的方向的话就是一棵树了). 那么考虑树型\(dp\),设\(f[i][ ...

  8. P4099 [HEOI2013]SAO

    P4099 [HEOI2013]SAO 贼板子有意思的一个题---我()竟然没看题解 有一张连成树的有向图,球拓扑序数量. 树形dp,设\(f[i][j]\)表示\(i\)在子树中\(i\)拓扑序上排 ...

  9. BZOJ 3167: [Heoi2013]Sao

    3167: [Heoi2013]Sao Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 96  Solved: 36[Submit][Status][D ...

随机推荐

  1. python基础一整型、bool、字符串

    1整型.bool.字符串 1.整型 --数字(int) ​ 用于比较运算的 ​ 32位 -2 ** 31 ~ 2 ** 31 -1 ​ 64位 -2 ** 63 ~ 2 ** 63 -1 ​ 基础运算 ...

  2. vs调试看窗口风格

    vs调试看窗口风格 技巧:在数值上右键,以16进制显示.

  3. SpringCloud解析之Zuul(二)

    本文基于Spring Cloud Edgware.SR6,Zuul版本1.3.1,解析Zuul的请求拦截机制,让大家对Zuul的原理有个大概的认识和了解.如有不对的地方,欢迎指正. 在上一期的Spri ...

  4. JS实现当前页弹出窗口,且页面变灰不可操作

    使用JS在当前页面在div中加载弹出窗口,并让当前页面变灰不可操作. 加载弹出窗口的div需将宽高设置成整个页面的大小,以覆盖当前页面的内容. opacity:0.6: 页面可见度设置为0.6(1为完 ...

  5. 个人永久性免费-Excel催化剂功能第18波-在Excel上也能玩上词云图

    这年头数据可视化日新月异,在Excel上做数据分析,最后一步,难免要搞个图表输出高大上一回,微软也深知此道,在Excel2016上更新了一大波图表功能,市场上很耀眼的词云图还是没加进来,虽然在各大的在 ...

  6. 【干货干货】configtxlator 工具介绍

    这一章我们对configtxlator做一个解说. 1, configtxlator 他是什么,有什么用? configtxlator 官方说明是用于生成Hyperledger结构通道配置的实用程序: ...

  7. Codeforces Round #565 (Div. 3)

    传送门 A. Divide it! •题意 给定一个数n, 每次可以进行下列一种操作 1.如果n可以被2整除,用n/2代替n 2.如果n可以被3整除,用2n/3代替n 3.如果n可以被5整除,用4n/ ...

  8. es6,@import一直报错 Can't resolve。。

    最近在项目中新增了一个按钮组的组件页面,但是在其他页面引入时,一直报错 引入方式:

  9. jumpserver1.4.1 安装过程

    # 修改字符集 localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8 echo 'LANG="zh_CN. ...

  10. 基于Docker的GitLab搭建

    今天写一些Docker搭建GitLab,好久没有写博客园了,今天又回来了,为了学习技术? 建议使用Linux内核系统,或者虚拟机,首先安装docker环境(菜鸟教程) 一.下载镜像文件 如果慢的话,可 ...