4013: [HNOI2015]实验比较

4013: [HNOI2015]实验比较

链接

分析:

  首先把等号用并查集合并起来。

  由于只存在最多一个质量不比i差的数,发现这是森林。若x<y,连边x->y。于是建虚拟根节点0。

  然后树形dp,f[i][j]表示第i棵子树内,分成了j段的方案数,即存在j-1个小于号。

  依次合并每个子树,假设一棵树内是a段,一棵树内是b段,合并后变成k段,$k in [max(a,b), a + b]$

  $f[i][k]=f[u][a] imes f[v][b] imes C_{k}^{a} imes C_{a}^{b-(k-a)}$

  后面一项的意义:此时相当于有k个盒子,有a个白球,b个黑球,每个盒子里不能同时出现两个黑球或者白球。那么先让每个白球选一个盒子放进去,黑球先补上空着的,最后多出的黑球放到以前白球的盒子里。

  复杂度:复杂度的话,看似O(N^4),但是,每个点对只会在其LCA处被枚举到并产生O(N)的运算量 精细地实现的话复杂度是O(N^3)的。by CRZbulabula 

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<map>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
 
inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}
inline int Read() {
    char ch = getchar();for(;ch!='<'&&ch!='='&&ch!='>'; ch=getchar());
    return (ch == '<' ? 1 : (ch == '=' ? 0 : 2));
}
 
const int N = 105, mod = 1e9 + 7;
struct Edge { int to, nxt; } e[20005];
int head[N], C[N][N], fa[N], ext[N][N], vis[N], deg[N], q[N], siz[N], f[N][N], tmp[N], En;
vector<int> vec[N];
 
inline void add_edge(int u,int v) {
    ++En; e[En].to = v, e[En].nxt = head[u]; head[u] = En; deg[v] ++;
}
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
inline void add(int &x,int y) { x += y; if (x >= mod) x -= mod; }
inline int mul(int x,int y) { return 1ll * x * y % mod; }
void dfs(int u) {
    siz[u] = f[u][1] = 1;
    for (int I = head[u]; I; I = e[I].nxt) {
        int v = e[I].to;
        dfs(v);
        for (int i = 0; i < siz[u]; ++i) 
            for (int j = 1; j <= siz[v]; ++j)    
                for (int k = max(i, j); k <= i + j; ++k) {
                    int A = mul(f[u][i + 1], f[v][j]), B = mul(C[k][i], C[i][j - k + i]);
                    add(tmp[k + 1], mul(A, B));
                }
        siz[u] += siz[v];
        for (int i = 0; i <= siz[u]; ++i) f[u][i] = tmp[i], tmp[i] = 0;      
    }
}
int main () {
    int n = read(), m = read();
    C[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
        C[i][0] = 1;
        for (int j = 1; j <= i; ++j) 
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
    }
    for (int i = 1; i <= n; ++i) fa[i] = i;
    for (int i = 1; i <= m; ++i) {
        int x = read(), ty = Read(), y = read();
        if (!ty) {
            x = find(x), y = find(y);
            if (x != y) fa[x] = y;
        }
        else if (ty == 1) vec[x].push_back(y);
        else vec[y].push_back(x);
    }
    for (int i = 1; i <= n; ++i) {
        int x = find(i);
        for (int sz = vec[i].size(), j = 0; j < sz; ++j) {
            int y = find(vec[i][j]);
            if (!ext[x][y]) add_edge(x, y), ext[x][y] = 1;
        }
    }
    for (int i = 1; i <= n; ++i) 
        if (deg[i] == 0 && fa[i] == i) add_edge(0, i);
    int L = 1, R = 0;
    q[++R] = 0;
    while (L <= R) {
        int u = q[L ++]; vis[u] = 1;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (!(--deg[v])) q[++R] = v;
        }
    }
    for (int i = 1; i <= n; ++i) 
        if (fa[i] == i && !vis[i]) { cout << 0; return 0; }
    dfs(0);
    int ans = 0;
    for (int i = 1; i <= siz[0]; ++i) add(ans, f[0][i]);
    cout << ans;
    return 0;
}
原文地址:https://www.cnblogs.com/mjtcn/p/10355670.html