[BZOJ 3167][HEOI 2013]SAO

题意

对一个长度为 \(n\) 的排列作出 \(n-1\) 种限制, 每种限制形如 "\(x\) 在 \(y\) 之前" 或 "\(x\) 在 \(y\) 之后". 且保证任意两点之间都有直接或间接的限制关系. 求方案数量.

\(n\le 1000\).

题解

Sword Art Online还行

拖了很久终于想起这题了...

首先我们发现这个限制关系是树状的, 那么我们尝试用子树来定义状态. 设 \(dp_{i,j}\) 表示以 \(i\) 为根的子树中的元素组成的合法排列且 \(i\) 在升序下排在第 \(j\) 名的方案数量. 那么我们要做的就是将两个排列在符合限制的条件下保序合并.

保序合并比较简单, 枚举一个排列在合并后的排列中占据哪些位置就可以了. 显然这是一个简单的组合数.

为了符合限制条件, 我们需要把在最终排列中排在当前根之前的结点和之后的结点区别对待. 枚举子树中有 \(j\) 个点排在根之前. 然后将限制分两类讨论:

  • 若限制是子结点先于根节点, 那么设根节点为 \(r\), 子结点为 \(s\), 有:

    \[dp_{r,i+j}={i+j-1\choose i-1}{\text{size}_r+\text{size}_s-i-j\choose \text{size}_s-j}dp_{r,i}\sum_{k=1}^jdp_{s,k}
    \]

  • 若限制是根结点先于子结点, 那么有:

    \[dp_{r,i+j}={i+j-1\choose i-1}{\text{size}_r+\text{size}_s-i-j\choose \text{size}_s-j}dp_{r,i}\sum_{k=j+1}^{\text{size}_s}dp_{s,k}
    \]

其中等式右侧(包括 \(\text{size}_r\) )都是合并 \(s\) 所在子树信息前的信息.

不难发现这个东西是个 \(O(n^3)\) 的爆炸复杂度. 但是转移式最后的和式是个前/后缀和的形式, 预处理一下就可以均摊 \(O(1)\) 得到那个和式的值了.

于是复杂度就变成 \(O(n^2)\) 了.

以及这题复杂度的分析, 我们直接看转移式会感觉它单次转移是 \(O(n^2)\) 的, 实际上 \(i\) 和 \(j\) 的枚举范围分别只有 \(\text{size}_r\) 和 \(\text{size}_s\). 总枚举量就是 "当前要合并的子树大小" 与 "已经合并过的兄弟子树大小之和" 的积. 那么对于 \(r\) 的转移枚举量其实就等于以 \(r\) 为LCA的点对数量. 那么整棵树的总枚举量就是 \(O(n^2)\) 了.

参考代码

然而排列是 \([0,n)\) 的...

#include <bits/stdc++.h>

const int MAXN=1010;
const int MOD=1e9+7; struct Edge{
int from;
int to;
int typ;
Edge* next;
};
Edge E[MAXN*2];
Edge* head[MAXN];
Edge* top=E; int n;
int tmp[MAXN];
int inv[MAXN];
int fact[MAXN];
int size[MAXN];
int pf[MAXN][MAXN];
int sf[MAXN][MAXN];
int dp[MAXN][MAXN]; int C(int,int);
void DFS(int,int);
int Pow(int,int,int);
void Insert(int,int,int); int main(){
int T;
scanf("%d",&T);
while(T--){
memset(dp,0,sizeof(dp));
memset(pf,0,sizeof(pf));
memset(sf,0,sizeof(sf));
memset(head,0,sizeof(head));
top=E;
scanf("%d",&n);
fact[0]=1;
for(int i=1;i<=n;i++)
fact[i]=1ll*fact[i-1]*i%MOD;
inv[n]=Pow(fact[n],MOD-2,MOD);
for(int i=n;i>=1;i--)
inv[i-1]=1ll*inv[i]*i%MOD;
for(int i=1;i<n;i++){
int a,b;
char op;
scanf("%d %c%d",&a,&op,&b);
Insert(a,b,op=='>');
Insert(b,a,op=='<');
}
DFS(0,-1);
printf("%d\n",sf[0][1]);
}
return 0;
} void DFS(int root,int prt){
size[root]=1;
dp[root][1]=1;
for(Edge* k=head[root];k!=NULL;k=k->next){
if(k->to!=prt){
DFS(k->to,root);
memset(tmp,0,sizeof(tmp));
if(k->typ){
for(int i=1;i<=size[root];i++){
for(int j=1;j<=size[k->to];j++){
tmp[i+j]=(tmp[i+j]+1ll*C(i+j-1,i-1)*C(size[root]+size[k->to]-i-j,size[k->to]-j)%MOD*dp[root][i]%MOD*pf[k->to][j])%MOD;
}
}
}
else{
for(int i=1;i<=size[root];i++){
for(int j=0;j<size[k->to];j++){
tmp[i+j]=(tmp[i+j]+1ll*C(i+j-1,i-1)*C(size[root]+size[k->to]-i-j,size[k->to]-j)%MOD*dp[root][i]%MOD*sf[k->to][j+1])%MOD;
}
}
}
size[root]+=size[k->to];
memcpy(dp[root],tmp,sizeof(tmp));
}
}
for(int i=1;i<=size[root];i++)
pf[root][i]=(dp[root][i]+pf[root][i-1])%MOD;
for(int i=size[root];i>=1;i--)
sf[root][i]=(dp[root][i]+sf[root][i+1])%MOD;
} inline void Insert(int from,int to,int typ){
top->from=from;
top->to=to;
top->typ=typ;
top->next=head[from];
head[from]=top++;
} inline int C(int n,int m){
return n<0||m<0||n<m?0:1ll*fact[n]*inv[m]%MOD*inv[n-m]%MOD;
} inline int Pow(int a,int n,int p){
int ans=1;
while(n>0){
if(n&1)
ans=1ll*a*ans%p;
a=1ll*a*a%p;
n>>=1;
}
return ans;
}

[BZOJ 3167][HEOI 2013]SAO的更多相关文章

  1. 解题:HEOI 2013 SAO

    题面 不好讲,直接上式子吧=.= 设$dp[i][j]$表示考虑完$i$的子树后$i$的排名为$j$的方案数,然后转移类似树形背包,具体来说是(这里假设子树在$i$后选,其实反过来还用这个式子答案也是 ...

  2. [HEOI 2013]SAO

    Description 题库连接 给你一个 \(n\) 个节点的有向树,问你这棵树的拓扑序个数,对大质数取模.多测,测试组数 \(T\). \(1\leq n\leq 1000, 1\leq T\le ...

  3. [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)

    [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并) 题面 给出一个n个节点m条边的森林,每个节点都有一个权值.有两种操作: Q x y k查询点x到点y路径上所有的权值中 ...

  4. [BZOJ 3173] [TJOI 2013] 最长上升子序列(fhq treap)

    [BZOJ 3173] [TJOI 2013] 最长上升子序列(fhq treap) 题面 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数 ...

  5. BZOJ 3236 AHOI 2013 作业 莫队+树状数组

    BZOJ 3236 AHOI 2013 作业 内存限制:512 MiB 时间限制:10000 ms 标准输入输出     题目类型:传统 评测方式:文本比较 题目大意: 此时己是凌晨两点,刚刚做了Co ...

  6. [BZOJ 3144][HNOI 2013] 切糕

    题目大意 切糕是 (p times q times r) 的长方体,每个点有一个违和感 (v_{x, y, z}).先要水平切开切糕(即对于每个纵轴,切面与其有且只有一个交点),要求水平上相邻两点的切 ...

  7. BZOJ 3167: [Heoi2013]Sao

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

  8. 【BZOJ 3165】【HEOI 2013】Segment

    往区间上覆盖一次函数,做法是用线段树维护标记永久化. 每次都忘了线段树要4倍空间,第一次交总是RE,再这么手残的话考场上就真的要犯逗了. #include<cstdio> #include ...

  9. [HEOI 2013 day2] SAO (树形动态规划)

    题目大意 给一棵N个节点的有向树(N <= 1000),求其拓扑序列个数. 思路 我们将任意一个点作为根,用dp[i][j]表示以节点i为根的子树满足节点i在第j个位置上的拓扑序列的个数.在求节 ...

随机推荐

  1. 解决U盘拷贝时提示文件过大问题(不能拷贝超过4个g的文件)

    为什么一个16G的U盘却拷不进一个4G大点的文件呢,想必很多朋友们都在疑问? 其实这跟U盘的磁盘格式有关,目前市面上常用的U盘大都是FAT32格式,我们可以查看U盘属性. 那么FAT32是什么呢? F ...

  2. SpringMVC之REST

    REST: 即 Representational State Transfer.(资源)表现层状态转化.是目前最流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展方便,所以正得到越来越多网 ...

  3. 表格行mouse经过时高亮显示

    昨天有解决网友一个问题<Repeater控件添加onmouseover和onmouseout事件>http://www.cnblogs.com/insus/p/3714013.html 这 ...

  4. AssemblyInfo.cs 详解

    前言 ? .net工程(包括Web和WinForm)的Properties文件夹下自动生成一个名为AssemblyInfo.cs的文件,一般情况下我们很少直接改动该文件.但我们实际上通过另一个形式操作 ...

  5. C# Hadoop学习笔记(二)—架构原理

    一,架构   二.名词解释 (一)NameNode(简称NN),Hadoop的主节点,负责侦听节点是否活跃,对外开放接口等.在未来的大数据处理过程中,由于访问量和节点数量的不断增多,需要该节点的处理能 ...

  6. java.lang.IllegalStateException: Restarter has not been initialized

    在IDEA导入devtools依赖实现热布署,启动后遇到如下问题 java.lang.IllegalStateException: Restarter has not been initialized ...

  7. 取石子游戏(hdu1527 博弈)

    取石子游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  8. python3.8 新特性

    https://docs.python.org/3.8/whatsnew/3.8.html python 3.8的新功能本文解释了与3.7相比,python 3.8中的新特性. 有关完整的详细信息,请 ...

  9. SQLServer 常见SQL笔试题之语句操作题详解

    SqlServer 常见SQL笔试题之语句操作题详解 by:授客 QQ:1033553122 测试数据库 CREATE DATABASE handWriting ON PRIMARY ( name = ...

  10. MVC与单元测试实践之健身网站(二)-管理员模块

    开始动手做这个项目时,发现无法做到完全的先设计.再编码,于是决定分模块进行,从管理员模块开始设计.编码,而且接口就已经改了好几次了. 管理员模块涉及的功能有登录和后台对管理员的维护,其中也涉及前端的开 ...