zoukankan      html  css  js  c++  java
  • [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$个点里,方案数共有$sum_{x=1}^{k}f[v][x]$种
    然后$u$本身有$f[u][j]$种方案
    根据乘法原理,把所有方案数乘起来,加到$f[u][j+k]$里
    然后$sum_{x=1}^{k}f[v][x]$显然可以前缀优化
    注意在合并完一个孩子之前要把开辅助数组记录答案然后再赋值到$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
    ", sum[0][n]);
        }
        return 0;
    }
  • 相关阅读:
    最长公共子序列
    小测试 炒书
    洛谷P1968 美元汇率
    洛谷P3611 [USACO17JAN]Cow Dance Show奶牛舞蹈
    【刷题】【树】【模拟】树网的核
    【刷题】【dp】地精的贸易
    【刷题】【模拟】复制cs
    【刷题】【模拟】3n+1
    【刷题】【dp】中国象棋
    【刷题】【搜索】新数独
  • 原文地址:https://www.cnblogs.com/ruoruoruo/p/7627161.html
Copyright © 2011-2022 走看看