[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. Java编程思想:通配符(后面有两个小节,研究的不够深入)

    import java.util.*; public class Test { public static void main(String[] args) { } } /* 15.9 边界 要点: ...

  2. 洛谷P3877 [TJOI2010]打扫房间 解题报告

    首先整理一下条件: 1.恰好进出每个需打扫的房间各一次 2.进出每个房间不能通过同一个门 (其实前两个条件是一回事) 3.要求每条路线都是一个闭合的环线 4.每条路线经过的房间数大于2 让你在一个n* ...

  3. React躬行记(9)——组件通信

    根据组件之间的嵌套关系(即层级关系)可分为4种通信方式:父子.兄弟.跨级和无级. 一.父子通信 在React中,数据是自顶向下单向流动的,而父组件通过props向子组件传递需要的信息是组件之间最常见的 ...

  4. Linux环境Oracle数据库访问出现ORA-01034错误:oracle not available解决

    首先将用户切换为Oracle用户登录,su - oracle; 进入:sqlplus / as sysdba: 问题现象: SQL> select * from dba_users; selec ...

  5. codewars[7]-python Friend or Foe?

    list中保留四字母的,然后return. 解 def friend(x): i = len(x) ii = [] a = 0 while a < i: if len(x[a]) == 4: i ...

  6. [leetcode] 55. Jump Game (Medium)

    原题 题目意思即 每一格代表你当前最多能再往后跳几次,从第一格开始,如果能跳到最后一格返回true,反之为false. 思路:用一个下标记录当前最多能跳到哪一格,遍历一遍 --> 如果当前格子不 ...

  7. Spring + RocketMQ使用

    本文所介绍环境为win7环境下运行, 从官方github中(https://github.com/alibaba/RocketMQ)下载RocketMQ-master.zip,版本为v3.5.8,解压 ...

  8. Java NIO ByteBuffer 的使用与源码研究

    一.结论 ByteBuffer 是Java NIO体系中的基础类,所有与Channel进行数据交互操作的都是以ByteBuffer作为数据的载体(即缓冲区).ByteBuffer的底层是byte数组, ...

  9. jango简介

    Django简介   Django框架简介 MVC框架和MTV框架 MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Mode ...

  10. Integrating Thymeleaf with Spring

    这个是基于注解的配置方式,基于配置文件的http://www.cnblogs.com/honger/p/6875148.html 一.整体结构图 二.web.xml文件,这里使用了注解的方式 < ...