[BZOJ3167][Heoi2013]Sao

Posted Elder_Giang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ3167][Heoi2013]Sao相关的知识,希望对你有一定的参考价值。

3167: [Heoi2013]Sao

Time Limit: 30 Sec  Memory Limit: 256 MB Submit: 188  Solved: 83 [Submit][Status][Discuss]

Description

 

WelcometoSAO(StrangeandAbnormalOnline)。这是一个VRMMORPG,含有n个关卡。但是,挑战不同关卡的顺序是一
个很大的问题。有n–1个对于挑战关卡的限制,诸如第i个关卡必须在第j个关卡前挑战,或者完成了第k个关卡才
能挑战第l个关卡。并且,如果不考虑限制的方向性,那么在这n–1个限制的情况下,任何两个关卡都存在某种程
度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。

Input

第一行,一个整数T,表示数据组数。对于每组数据,第一行一个整数n,表示关卡数。接下来n–1行,每行为“i 
sign j”,其中0≤i,j≤n–1且i≠j,sign为“<”或者“>”,表示第i个关卡必须在第j个关卡前/后完成。
T≤5,1≤n≤1000

Output

对于每个数据,输出一行一个整数,为攻克关卡的顺序方案个数,mod1,000,000,007输出。

Sample Input

5
10
5 > 8
5 > 6
0 < 1
9 < 4
2 > 5
5 < 9
8 < 1
9 > 3
1 < 7
10
6 > 7
2 > 0
9 < 0
5 > 9
7 > 0
0 > 3
7 < 8
1 < 2
0 < 4
10
2 < 0
1 > 4
0 > 5
9 < 0
9 > 3
1 < 2
4 > 6
9 < 8
7 > 1
10
0 > 9
5 > 6
3 > 6
8 < 7
8 > 4
0 > 6
8 > 5
8 < 2
1 > 8
10
8 < 3
8 < 4
1 > 3
1 < 9
3 < 7
2 < 8
5 > 2
5 < 6
0 < 9

Sample Output

2580
3960
1834
5208
3336
 
可以发现把关系看成边的话这是一颗树
设$f[i][j]$表示在以$i$为根的子数中,根节点是第$j$大
那么考虑每次和一颗子树的答案合并(显然合并子树的顺序不影响答案)
对于每一个$f[u][j]$来说,当它在合并时孩子$v$时
先不考虑$u$和$v$两颗树本身的以及$u$和$v$之间的大小关系,那么可以
枚举子树中前$k$小的点比根小,那么这一步就有$C_{j+k-1}^{k}*C_{siz[u]+siz[v]-j-k}^{siz[v]-k}$种方案
这是因为把一个子树从小到大摊成一个序列后
$u$前面有$j+k-1$个位置放点,其中选$k$个给$v$,$u$后面有&siz[u]+siz[v]-j-k&个位置放点,其中选$siz[v]-k$个给孩子
然后要考虑$u$和$v$两颗子树本身之间的关系
以$u$在$v$后面为例
$v$肯定在插入$u$之前的那$k$个点里,方案数共有$\sigma_{x=1}^{k}f[v][x]$种
然后$u$本身有$f[u][j]$种方案
根据乘法原理,把所有方案数乘起来,加到$f[u][j+k]$里
注意在合并完一个孩子之前要把开辅助数组记录答案然后再赋值到$f$数组里
具体看代码
#pragma GCC optimize("O2")
#include <cstdio>
#include <cstring>
char buf[10000000], *ptr = buf - 1;
inline int readint(){
    int n = 0;
    char ch = *++ptr;
    while(ch < 0 || ch > 9) ch = *++ptr;
    while(ch <= 9 && ch >= 0){
        n = (n << 1) + (n << 3) + (ch ^ 48);
        ch = *++ptr;
    }
    return n;
}
typedef long long ll;
const int maxn = 1000 + 10, mod = 1000000007;
inline void add(ll &x, ll y){
    x = (x + y) % mod;
}
ll C[maxn][maxn];
struct Edge{
    int to, val, next; // (val = 1) <=> ‘>‘ ; (val = 0) <=> ‘<‘
    Edge(){}
    Edge(int _t, int _v, int _n): to(_t), val(_v), next(_n){}
}e[maxn * 2];
int fir[maxn], cnt;
inline void ins(int u, int v, int w){
    e[++cnt] = Edge(v, w, fir[u]); fir[u] = cnt;
    e[++cnt] = Edge(u, w ^ 1, fir[v]); fir[v] = cnt;
}
int n;
ll sum[maxn][maxn];
ll f[maxn][maxn], tp[maxn];
int siz[maxn];
void dfs(int u, int fa){
    siz[u] = f[u][1] = 1;
    for(int v, i = fir[u]; i; i = e[i].next){
        v = e[i].to;
        if(v == fa) continue;
        dfs(v, u);
        for(int j = siz[u] + siz[v]; j; j--) tp[j] = 0;
        // u > v
        if(e[i].val == 1)
            for(int j = 1; j <= siz[u]; j++)
                for(int k = 1; k <= siz[v]; k++)
                    add(tp[j + k], C[j + k - 1][k] * C[siz[u] + siz[v] - j - k][siz[v] - k] % mod * f[u][j] % mod * sum[v][k]);
        else
            for(int j = 1; j <= siz[u]; j++)
                for(int k = 0; k < siz[v]; k++)
                    add(tp[j + k], C[j + k - 1][k] * C[siz[u] + siz[v] - j - k][siz[v] - k] % mod * f[u][j] % mod * (sum[v][siz[v]] - sum[v][k] + mod));
        siz[u] += siz[v];
        for(int j = 1; j <= siz[u]; j++) f[u][j] = tp[j];        
    }
    sum[u][0] = 0;
    for(int i = 1; i <= siz[u]; i++){
        sum[u][i] = sum[u][i - 1] + f[u][i];
        if(sum[u][i] >= mod) sum[u][i] -= mod;
    }
}
void init(){
    n = readint();
    cnt = 0;
    for(int i = 0; i < n; i++) fir[i] = 0;
    int u, v;
    char ch;
    for(int i = 1; i < n; i++){
        u = readint();
        ch = *++ptr;
        while(ch != < && ch != >) ch = *++ptr;
        v = readint();
        if(ch == >) ins(u, v, 1);
        else ins(u, v, 0);
    }
    for(int i = 0; i < n; i++)
        for(int j = 1; j <= n; j++)
            f[i][j] = 0;
}
void init_C(){
    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];
            if(C[i][j] >= mod) C[i][j] -= mod;
        }
}
int main(){
    fread(buf, sizeof(char), sizeof(buf), stdin);
    init_C();
    int T = readint();
    while(T--){
        init();
        dfs(0, -1);
        printf("%lld\n", sum[0][n]);
    }
    return 0;
}

 

以上是关于[BZOJ3167][Heoi2013]Sao的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 3167: [Heoi2013]Sao

BZOJ3167/4824[Heoi2013]Sao/[Cqoi2017]老C的键盘

bzoj3167 [Heoi2013]Sao

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

bzoj 3167 SAO

[HEOI2013]SAO