P4099 [HEOI2013]SAO(树形dp)
我们设$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$的范围需要分类:
- $u<v$(拓扑序)$j\in[k-i+1,siz[v]]$,即点$v$不能填充到拓扑序$<u$的地方
- $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)的更多相关文章
- [BZOJ3167][P4099][HEOI2013]SAO(树形DP)
题目描述 Welcome to SAO ( Strange and Abnormal Online).这是一个 VR MMORPG, 含有 n 个关卡.但是,挑战不同关卡的顺序是一个很大的问题. 有 ...
- 3167: [Heoi2013]Sao [树形DP]
3167: [Heoi2013]Sao 题意: n个点的"有向"树,求拓扑排序方案数 Welcome to Sword Art Online!!! 一开始想错了...没有考虑一个点 ...
- BZOJ 3167 [Heoi2013]Sao ——树形DP
BZOJ4824的强化版. 改变枚举的方案,使用前缀和进行DP优化. 然后复杂度就是$O(n^2)$了. #include <map> #include <cmath> #in ...
- 洛谷 4099 [HEOI2013]SAO——树形DP
题目:https://www.luogu.org/problemnew/show/P4099 结果还是看了题解才会…… 关键是状态,f[ i ][ j ] 表示 i 子树. i 号点是第 j 个出现的 ...
- P4099 [HEOI2013]SAO
P4099 [HEOI2013]SAO 贼板子有意思的一个题---我()竟然没看题解 有一张连成树的有向图,球拓扑序数量. 树形dp,设\(f[i][j]\)表示\(i\)在子树中\(i\)拓扑序上排 ...
- 洛谷 P4099 - [HEOI2013]SAO(树形 dp)
题面传送门 题意: 有一个有向图 \(G\),其基图是一棵树 求它拓扑序的个数 \(\bmod (10^9+7)\) \(n \in [1,1000]\) 如果你按照拓扑排序的方法来做,那恐怕你已经想 ...
- 洛谷P4099 [HEOI2013]SAO(树形dp)
传送门 HEOI的题好珂怕啊(各种意义上) 然后考虑树形dp,以大于为例 设$f[i][j]$表示$i$这个节点在子树中排名第$j$位时的总方案数(因为实际只与相对大小有关,与实际数值无关) 我们考虑 ...
- 洛谷$P4099\ [HEOI2013]\ SAO\ dp$
正解:树形$dp$ 解题报告: 传送门$QwQ$. 考虑设$f_i$表示点$i$的子树内的拓扑序排列方案数有多少个. 发现这样不好合并儿子节点和父亲节点.于是加一维,设$f_{i,j}$表示点$i$的 ...
- [HEOI2013]SAO(树上dp,计数)
[HEOI2013]SAO (这写了一个晚上QAQ,可能是我太蠢了吧.) 题目说只有\(n-1\)条边,然而每个点又相互联系.说明它的结构是一个类似树的结构,但是是有向边连接的,题目问的是方案个数,那 ...
随机推荐
- php学习十:继承
在php中,我们常常会定义许多类,当多个类里面的方法或者属性出现重复的时候,会常常造成代码重复和冗杂的弊端,这个时候,我们可以用到继承(extends) 继承的特性: * 1.子类可以扩充属性* 2. ...
- redis学习之集群报错Node is not empty
遇到的问题及解决办法 在redis.conf里bind 真机ip后,接着重新执行每个redis.conf,最后再创建集群,但报错,如下图所示: 图中报的错即: [ERR] Node 192.168.1 ...
- bootstrap之辅助类
类 描述 .pull-left 元素浮动到左边 .pull-right 元素浮动到右边 .center-block 设置元素为 display:block 并居中显示 .clearfix 清除浮动 . ...
- ICMP重定向及其攻防技术
1.ICMP重定向概念: ICMP重定向技术,是用来提示主机改变自己的主机路由从而使路由路径最优化的一种ICMP报文.其概念理解的要义是原主机路由不是最佳路由,而其默认网关提醒主机优化自身的主机路由而 ...
- 【BZOJ3387】[Usaco2004 Dec]Fence Obstacle Course栅栏行动 线段树
[BZOJ3387][Usaco2004 Dec]Fence Obstacle Course栅栏行动 Description 约翰建造了N(1≤N≤50000)个栅栏来与牛同乐.第i个栅栏的z坐标为[ ...
- [css]演示:纯CSS实现的右侧底部简洁悬浮效果
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <meta name ...
- Python - 3.6 学习二
Python 的高级特性 切片 对于指定索引范围取值的操作,Python提供了slice方法,类似于Excel中数据透视表的切片器. >>> L = ['Michael', 'Sar ...
- postgresql----Btree索引
当表数据量越来越大时查询速度会下降,像课本目录一样,在表的条件字段上创建索引,查询时能够快速定位感兴趣的数据所在的位置.索引的好处主要有加速带条件的查询,删除,更新,加速JOIN操作,加速外键约束更新 ...
- 传智播客京东商城移动web开发
1.源码笔记 我的源码+笔记(很重要):链接: https://pan.baidu.com/s/1eScieps 密码: 3vyr 感谢传智播客项目相关视频:1.6天链接: https://pan.b ...
- CH1402 后缀数组【Hash】【字符串】【二分】
1402 后缀数组 0x10「基本数据结构」例题 描述 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围.在本题中,我们希望使用快排.Hash与二分实现 ...