P4099 [HEOI2013]SAO

我们设$f[u][k]$表示以拓扑序编号为$k$的点$u$,以$u$为根的子树中的元素所组成的序列方案数

蓝后我们在找一个以$v$为根的子树。

我们的任务就是在合并这两棵树时维护$f[u][k]$

合并时,$v$的元素可能全在点$u$的前/后面,也可能都有。

分类讨论:

1.当有$p(p\in [0,siz[v]])$个元素插入到点$u$(拓扑序)前面时

我们知道插入后点$u$的拓扑序为$k$

那么插入前的拓扑序即为$k-p$

∴插入前子树$u$对应的状态就是$f[u][k-p]$

设$i=k-p$

那么插入的方案数就等价于在$k-1$位置($u$是固定最后的)中选择$i-1$个位置$*f[u][i]$

$=C(k-1,i-1)*f[u][i]$

其中$i\in [1,min(siz[u],k)]$

2.当有$p(p\in [0,siz[v]])$个元素插入到点$u$(拓扑序)后面时

可以这样表示$p=siz[v]-(k-i)=siz[v]-k+i$

而原来$u$(拓扑序)后面的元素共有$siz[u]-i$个

∴方案数$=C(siz[u]-i+p,p)*f[v][j]=C(siz[u]+siz[v]-k,siz[u]-i)*f[v][j]$

$j$的范围需要分类:

  1. $u<v$(拓扑序)$j\in[k-i+1,siz[v]]$,即点$v$不能填充到拓扑序$<u$的地方
  2. $u>v$(拓扑序)$j\in[1,k-i]$

整理一下:

$f[u][k]=\sum_{i=1}^{min(siz[u],k)}\sum_{j}(分类讨论)*C(k-1,i-1)*C(siz[u]+siz[v]-k,siz[u]-i)$

然鹅这是$O(n^{3})$

发现是$j$是连续一段区间,可以前缀和处理

蓝后就愉快地变成$O(n^{2})$了

end.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#define re register
using namespace std;
typedef long long ll;
void read(int &x){
char c=getchar(); x=;
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=(x<<)+(x<<)+(c^),c=getchar();
}int wt[];
void output(ll x){
if(!x) {putchar(); return;}
int l=;
while(x) wt[++l]=x%,x/=;
while(l) putchar(wt[l--]+);
}
int min(int &a,int &b) {return a<b?a:b;}
const int p=1e9+;
ll mod(ll a){return a<p?a:a-p;}
#define N 1001
int t,n,siz[N];
ll f[N][N],C[N][N],sum[N][N],ans;
int cnt,hd[N],nxt[N<<],ed[N],poi[N<<],dir[N<<];
bool vis[N];
void adde(int x,int y,int v){
nxt[ed[x]]=++cnt; hd[x]=hd[x]? hd[x]:cnt;
ed[x]=cnt; poi[cnt]=y; dir[cnt]=v;
}
void clears(){//清空数据
for(re int i=;i<=n;++i){
f[i][]=; sum[i][]=;
vis[i]=; siz[i]=;
hd[i]=ed[i]=;
nxt[i]=nxt[i+n]=;
for(re int j=;j<=n;++j) f[i][j]=sum[i][j]=;
}cnt=ans=;
}
void dfs(int u){
vis[u]=;
for(int z=hd[u];z;z=nxt[z]){
int v=poi[z];
if(vis[v]) continue;
dfs(v);
if(dir[z]){//u<v(拓扑序)
for(int k=siz[u]+siz[v];k>=;--k){
ll tmp=;
for(int i=min(siz[u],k);i>=;--i){
int l=k-i+,r=siz[v]; //下面只要修改l,r即可
if(l>r) continue;
ll r1=mod(sum[v][r]-sum[v][l-]+p);
ll r2=C[k-][i-]*C[siz[u]+siz[v]-k][siz[u]-i]%p;
tmp=mod(tmp+f[u][i]*r1%p*r2%p);
}f[u][k]=tmp;
}
}else{//u>v(拓扑序)
for(int k=siz[u]+siz[v];k>=;--k){
ll tmp=;
for(int i=min(siz[u],k);i>=;--i){
int l=,r=k-i;
if(l>r) continue;
ll r1=mod(sum[v][r]-sum[v][l-]+p);
ll r2=C[k-][i-]*C[siz[u]+siz[v]-k][siz[u]-i]%p;
tmp=mod(tmp+f[u][i]*r1%p*r2%p);
}f[u][k]=tmp;
}
}siz[u]+=siz[v];
}
for(int i=;i<=siz[u];++i)//维护前缀和
sum[u][i]=mod(sum[u][i-]+f[u][i]);
}
int main(){
read(t); char opt[]; int q1,q2,q3;
for(re int i=;i<N;++i)//组合数预处理
for(re int j=;j<=i;++j)
C[i][j]=(!j||j==i)?:mod(C[i-][j]+C[i-][j-]);
while(t--){
read(n); clears();
for(re int i=;i<n;++i){
read(q1); scanf("%s",opt); read(q2);
q3=(opt[]=='<'); ++q1; ++q2;
adde(q1,q2,q3); adde(q2,q1,q3^);
}dfs();
for(re int i=;i<=n;++i) ans=mod(ans+f[][i]);
output(ans); putchar('\n');
}return ;
}

P4099 [HEOI2013]SAO(树形dp)的更多相关文章

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

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

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

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

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

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

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

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

  5. P4099 [HEOI2013]SAO

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

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

    题面传送门 题意: 有一个有向图 \(G\),其基图是一棵树 求它拓扑序的个数 \(\bmod (10^9+7)\) \(n \in [1,1000]\) 如果你按照拓扑排序的方法来做,那恐怕你已经想 ...

  7. 洛谷P4099 [HEOI2013]SAO(树形dp)

    传送门 HEOI的题好珂怕啊(各种意义上) 然后考虑树形dp,以大于为例 设$f[i][j]$表示$i$这个节点在子树中排名第$j$位时的总方案数(因为实际只与相对大小有关,与实际数值无关) 我们考虑 ...

  8. 洛谷$P4099\ [HEOI2013]\ SAO\ dp$

    正解:树形$dp$ 解题报告: 传送门$QwQ$. 考虑设$f_i$表示点$i$的子树内的拓扑序排列方案数有多少个. 发现这样不好合并儿子节点和父亲节点.于是加一维,设$f_{i,j}$表示点$i$的 ...

  9. [HEOI2013]SAO(树上dp,计数)

    [HEOI2013]SAO (这写了一个晚上QAQ,可能是我太蠢了吧.) 题目说只有\(n-1\)条边,然而每个点又相互联系.说明它的结构是一个类似树的结构,但是是有向边连接的,题目问的是方案个数,那 ...

随机推荐

  1. Java的String详解

    Java的String详解 博客分类: Java javaStringString详解常用方法  Java的String类在开发时经常都会被使用到,由此可见String的重要性.经过这次认真仔细的学习 ...

  2. php实现注册审核功能

    本章主要实现注册之后审核通过的功能,共这几部分组成: 1. 创建数据库:mydb数据库的user表   注:isok判断是否通过审核,1为通过,0为未通过. 显示效果: 2.首先做注册界面:zhuce ...

  3. Change Base

    Given an integer m in base B (2 ≤ B ≤ 10) (m contains no more than 1000 digits), find the value of t ...

  4. MQTT协议笔记之连接和心跳

    前言 本篇会把连接(CONNECT).心跳(PINGREQ/PINGRESP).确认(CONNACK).断开连接(DISCONNECT)和在一起. CONNECT 像前面所说,MQTT有关字符串部分采 ...

  5. postgresql----数据库表约束----NOT NULL,DEFAULT,CHECK

    数据库表有NOT NULL,DEFAULT,CHECK,UNIQUE,PRIMARY KEY,FOREIGN KEY六种约束. 一.NOT NULL ---- 非空约束 NULL表示没有数据,不表示具 ...

  6. 9.SQL存储过程实例详解

    本文用3个题目,从建立数据库到创建存储过程,详细讲解数据库的功能. 题目1 学校图书馆借书信息管理系统建立三个表:学生信息表:student 字段名称 数据类型 说明 stuID char(10) 学 ...

  7. java 中关于System.out.println()的问题

    Java 的输出知识 1.System.out.println()不能直接写在类中,例如: 因为在 Class A{ //成员变量 //构造方法 //普通方法 //内部类 } 如果硬是想使用Syste ...

  8. 使用 Sonar 进行代码质量管理

    参考资料: 使用 Sonar 进行代码质量管理 SonarQube的安装.配置与使用 SonarLint(Sonar) 代码质量管理

  9. 导出Excel过程中遇到的问题java.lang.NoClassDefFoundError: org/apache/xmlbeans/XmlException

    尝试实现java导出功能时遇到如下报错: org.apache.jasper.JasperException: javax.servlet.ServletException: java.lang.No ...

  10. Oracle开发 之 主-外键约束FK及约束的修改

    试验环境: 1)数据库版本:oracle 11.2.0.4 2)建表脚本:以scott的dept及emp表为基础. 父表:dept -- Create table create table DEPT ...