传送门

这题神坑啊……明明是你菜

首先大家都知道原题等价于给每个点分配一个$1$~$n$且两两不同的权值,同时还需要满足一些大于/小于关系的方案数。

先看一眼数据范围,既然写明了$n\le 1000$,那就应该是什么$O(n^2)$的做法了。显然这个东西只能是个DP,考虑到题中给出的是一个树形结构,那么就可以利用子树的相对独立性进行DP:设$f_{i,j}$表示以$i$为根的子树中有$j$个点的权值大于$i$的权值时的方案数,显然最终答案就是$\sum_{i}f_{root,i}$。

然后考虑怎么求出答案。通常树形DP都是自底向上逐个合并来得到每个点的DP值的,但注意这个DP无法做到像普通的树形DP一样可以快速(比如$O(1)$或者$O(\log^2 n)$之类)合并。不过这个DP还是可以比较高效地合并的,设要合并的两个子树大小分别为$n,m$,那么我们就可以通过枚举每一对$(i,j)$对应的$f_{\dots,i}$和$f_{\dots,j}$对合并后的DP数组的贡献来在$O(nm)$的时间内得到它们合并后的DP数组。

具体的合并过程就不写了,大体思路是先枚举最后$i$的权值在整个子树中的排名,然后枚举另一棵子树中有几个点权值比$i$的权值小,最后换元得到枚举$(i,j)$的形式。顺便一提,这个DP方程还需要一个前缀和优化才能做到$O(nm)$合并,还有大于和小于两种情况需要分开处理。

 /**************************************************************
Problem: 3167
User: _Angel_
Language: C++
Result: Accepted
Time:3984 ms
Memory:8792 kb
****************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=,p=;
int C[maxn][maxn];
struct DP{
int f[maxn],n;
void clear(){
memset(f,,sizeof(f));
n=;
f[]=;
}
DP &operator+=(const DP &b){
static int g[maxn];
memset(g,,sizeof(g));
for(int i=;i<n;i++)for(int j=,tmp=;j<=b.n;j++){
tmp=(tmp+b.f[j-])%p;
g[i+j]=(g[i+j]+(long long)f[i]*tmp%p*C[i+j][j]%p*C[n+b.n-i-j-][b.n-j]%p)%p;
}
memcpy(f,g,sizeof(f));
n+=b.n;
return *this;
}
DP &operator*=(const DP &b){
static int g[maxn];
memset(g,,sizeof(g));
int sum=;
for(int j=;j<b.n;j++)sum=(sum+b.f[j])%p;
for(int i=;i<n;i++)for(int j=,tmp=sum;j<b.n;j++){
g[i+j]=(g[i+j]+(long long)f[i]*tmp%p*C[i+j][j]%p*C[n+b.n-i-j-][b.n-j]%p)%p;
tmp=(tmp-b.f[j]+p)%p;
}
memcpy(f,g,sizeof(f));
n+=b.n;
return *this;
}
}f[maxn];
void dfs(int);
vector<int>G[maxn];
vector<bool>W[maxn];
int T,n,prt[maxn];
int main(){
C[][]=;
for(int i=;i<=;i++)for(int j=;j<=i;j++){
C[i][j]=C[i-][j];
if(j)C[i][j]=(C[i][j]+C[i-][j-])%p;
}
scanf("%d",&T);
while(T--){
scanf("%d",&n);
memset(prt,,sizeof(prt));
for(int i=;i<=n;i++){
G[i].clear();
W[i].clear();
f[i].clear();
}
for(int i=,x,y;i<n;i++){
char c;
scanf("%d %c%d",&x,&c,&y);
x++;
y++;
G[x].push_back(y);
W[x].push_back(c=='>');
G[y].push_back(x);
W[y].push_back(c=='<');
}
dfs();
int ans=;
for(int i=;i<n;i++)ans=(ans+f[].f[i])%p;
printf("%d\n",ans);
}
return ;
}
void dfs(int x){
for(int i=;i<(int)G[x].size();i++)if(G[x][i]!=prt[x]){
prt[G[x][i]]=x;
dfs(G[x][i]);
if(W[x][i])f[x]+=f[G[x][i]];
else f[x]*=f[G[x][i]];
}
}

bzoj3167 [Heoi2013]Sao的更多相关文章

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

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

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

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

  3. 【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的键盘

    [BZOJ3167][Heoi2013]Sao Description WelcometoSAO(StrangeandAbnormalOnline).这是一个VRMMORPG,含有n个关卡.但是,挑战 ...

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

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

  5. P4099 [HEOI2013]SAO

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

  6. BZOJ 3167: [Heoi2013]Sao

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

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

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

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

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

  9. 【做题记录】 [HEOI2013]SAO

    P4099 [HEOI2013]SAO 类型:树形 \(\text{DP}\) 这里主要补充一下 \(O(n^3)\) 的 \(\text{DP}\) 优化的过程,基础转移方程推导可以参考其他巨佬的博 ...

随机推荐

  1. CISSP一次通过指南(文末附福利)

    2017年12月19日,在上海黄浦区汉口路亚洲大厦17层通过了CISSP认证考试,拖拉了一年,终于成绩还算令人满意,为攒人品将自己一年多的复习心得和大家分享,希望能够帮到需要考证的朋友. 本文作者:i ...

  2. jmeter插件之自定义场景图(万能场景设计)

    添加扩展插件 自定义线程组:jp@gc - Ultimate Thread Group 此线程组功能强大,可以实现多种场景设置,添加路径如图 参数含义解释 Start Threads Count:当前 ...

  3. Found an unexpected Mach-O header code: 0x72613c21

    在按照第三方sdk文档中的Emedded Binaries 中加入了他们的framework,在删除这下面的对应的framework后,问题就得到了解决 发下有个英文的页面也是涉及这个问题的, 描述的 ...

  4. JS滑动到页面底部

    window.scrollTo(0, document.documentElement.clientHeight); 该 window 对象在DOM有一个 scrollTo 滚动到打开窗口 的任意位置 ...

  5. LeetCode题解-147 对链表进行插入排序 Medium

    对链表进行插入排序. 插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插 ...

  6. Monkey捕获Crash原理

    Android的RuntimeInit.commonInit中会设置UncaughtHandler ActivityManagerService(AMS)在执行handleApplicationCra ...

  7. python中的sort方法

    Python中的sort()方法用于数组排序,本文以实例形式对此加以详细说明: 一.基本形式 列表有自己的sort方法,其对列表进行原址排序,既然是原址排序,那显然元组不可能拥有这种方法,因为元组是不 ...

  8. Hadoop HDFS概念学习系列之HDFS升级和回滚机制(十二)

    不多说,直接上干货! HDFS升级和回滚机制 作为一个大型的分布式系统,Hadoop内部实现了一套升级机制,当在一个集群上升级Hadoop时,像其他的软件升级一样,可能会有新的bug或一些会影响现有应 ...

  9. wordpress常用标记

    博客地址:bloginfo('url'); 博客名称:bloginfo("name"); 博客描述:bloginfo("decription"); 文章内容: ...

  10. dos命令行运行.class源文件错误解决办法

    dos命令行运行java源文件 public static void main(String[] args) throws IOException { // TODO Auto-generated m ...