[HEOI2013]SAO

Posted ac-evil

tags:

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

[HEOI2013]SAO

  这道题是个不错的计数题,考察了调换求和顺序再前缀和优化,难点在状态设计,比较考察思维。
  一句话题意:给你一棵数,树边为有向边,求其拓扑序数。
  对DAG求拓扑数是一个NP问题,但是这里保证是一棵树,所以我们可以用树形DP来求解。
  状态的设计上,光设结点编号\(u\)不够,还需要设计一维\(i\)表示结点\(u\)在以\(u\)为根的子树中的拓扑序的第\(i\)位,这样我们就可以写转移方程了。
  对于\(u \rightarrow v\)
\[ F'[u][k] = \Sigma_v\in son F[u][i]\times F[v][j] \times \tbinomk-1i-1 \times \tbinomsize_u+size_v-ksize_u-i,i \leq k \leq i+j-1 \]
  对于\(u \leftarrow v\)
\[ F'[u][k] = \Sigma_v\in son F[u][i]\times F[v][j] \times \tbinomk-1i-1 \times \tbinomsize_u+size_v-ksize_u-i,i+j \leq k \leq size_v+i \]
  目标:
\[ \Sigma_i=1^N F[1][i] \]
  (所有结点的下标自动+1)
  发现三重循环铁定不行,调换下\(j\)\(k\)的顺序,发现\(F[v][j]\)可以前缀和处理,削掉一维。
  事实上这样做后整体的复杂度由\(\textO(N^3)\)降为\(\textO(N^2)\)。(仔细研读下面的代码发现实际上处理次数为点对数)
  细节看码。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define INF (1 << 30)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b);

inline int read() 
    int w = 0, f = 1; char c;
    while (!isdigit(c = getchar())) f = c == '-' ? -1 : f;
    while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ 48), c = getchar();
    return w * f;


inline int read_ch() 
    char c;
    while (c = getchar(), c != '>' && c != '<');
    return c == '<';


const int maxn = 1000 + 5;
const int MOD = 1e9 + 7;

struct Edge 
    int v, w, pre;
 e[maxn << 1];
int m, G[maxn];
void clear() 
    m = 0;
    memset(G, -1, sizeof(G));

void add(int u, int v, int w) 
    e[m++] = (Edge)v, w, G[u];
    G[u] = m-1;


int T, N;
int f[maxn][maxn], g[maxn], C[maxn][maxn];

void inc(int &a, int b) 
    a += b;
    if (a >= MOD) a -= MOD;


int dec(int a) 
    if (a < 0) a += MOD;
    return a;


void init() 
    C[0][0] = 1;
    for (register int i = 1; i <= 1000; i++) 
        C[i][0] = 1;
        for (register int j = 1; j <= i; j++)
            C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD;
    


int size[maxn];

void dfs(int u, int fa) 
    size[u] = 1;
    f[u][1] = 1;
    for (register int i = G[u]; ~i; i = e[i].pre)  \\ 这里实际上相当于u<->v之间的拓扑序合并起来
        int v = e[i].v;
        if (v == fa) continue;
        dfs(v, u);
        memcpy(g, f[u], sizeof(g));
        memset(f[u], 0, sizeof(f[u]));
        if (e[i].w) 
            for (register int i = 1; i <= size[u]; i++)
                for (register int k = i; k <= i+size[v]-1; k++)
                    inc(f[u][k], (ll)g[i] * dec(f[v][size[v]]-f[v][k-i]) % MOD * C[k-1][i-1] % MOD * C[size[u]+size[v]-k][size[u]-i] % MOD);
         else 
            for (register int i = 1; i <= size[u]; i++)
                for (register int k = i+1; k <= size[v] + i; k++)
                    inc(f[u][k], (ll)g[i] * dec(f[v][k-i]) % MOD * C[k-1][i-1] % MOD * C[size[u]+size[v]-k][size[u]-i] % MOD);
        
        size[u] += size[v];
    
    for (register int i = 1; i <= size[u]; i++) inc(f[u][i], f[u][i-1]);


int main() 
    init();
    T = read();
    while (T--) 
        N = read();
        clear();
        for (register int i = 1; i < N; i++) 
            int u = read()+1, opt = read_ch(), v = read()+1;
            add(u, v, opt); add(v, u, !opt);
        

        dfs(1, 1);

        printf("%d\n", f[1][N]);
    

    return 0;

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

[HEOI2013]SAO

P4099 [HEOI2013]SAO

BZOJ 3167: [Heoi2013]Sao

[HEOI 2013]SAO

[HEOI2013]SAO

[HEOI2013]SAO