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] \times f[v][b] \times C_{k}^{a} \times 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; }