1112 字
6 分钟
BZOJ 3167 [Heoi2013] Sao
题目描述
WelcometoSAO(StrangeandAbnormalOnline)。这是一个VRMMORPG,含有n个关卡。但是,挑战不同关卡的顺序是一 个很大的问题。有n–1个对于挑战关卡的限制,诸如第i个关卡必须在第j个关卡前挑战,或者完成了第k个关卡才 能挑战第l个关卡。并且,如果不考虑限制的方向性,那么在这n–1个限制的情况下,任何两个关卡都存在某种程 度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。
输入格式
第一行,一个整数T,表示数据组数。对于每组数据,第一行一个整数n,表示关卡数。接下来n–1行,每行为“i sign j”,其中且,sign为“<”或者“>”,表示第i个关卡必须在第j个关卡前/后完成。
输出格式
对于每个数据,输出一行一个整数,为攻克关卡的顺序方案个数,mod1,000,000,007输出。
样例输入
5105 > 85 > 60 < 19 < 42 > 55 < 98 < 19 > 31 < 7106 > 72 > 09 < 05 > 97 > 00 > 37 < 81 < 20 < 4102 < 01 > 40 > 59 < 09 > 31 < 24 > 69 < 87 > 1100 > 95 > 63 > 68 < 78 > 40 > 68 > 58 < 21 > 8108 < 38 < 41 > 31 < 93 < 72 < 85 > 25 < 60 < 9
样例输出
25803960183452083336
题解
先做了ALO, 又做了SAO。
然后就突然想看刀剑了, 虽然看来好几遍了, 不过河北的省选真会玩儿。
什么时候来个GGO就好了。
回到这道题:
首先将本题转化为一个树上问题(这tm的不是显然吗)。
然后我们发现答案只与每对点的遍历数序有关。
那么我们定义为一为根节点的子树中有个比他小。
那么假设的子节点要求小于
那么以为根的子树中有可能有 个数比小
那么在考虑小于他的数的合并顺序,
由插板法得
同理小于它的为
综上转移方程为
大于与此类似
不知道为什么数组越界过来
#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int MAXN = 1005;const int MOD = 1000000007;inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}struct edge{ int END, next, v;}v[MAXN << 1];int first[MAXN], p;void add(int a, int b, int c){ v[p].END = b; v[p].next = first[a]; v[p].v = c; first[a] = p++;}long long f[MAXN][MAXN];long long g[MAXN];long long Sum[MAXN][MAXN];long long C[MAXN][MAXN];int size[MAXN];void dfs(int rt, int fa){ size[rt] = f[rt][0] = 1; for (int i = first[rt]; i != -1; i = v[i].next) { if (v[i].END == fa) continue; dfs(v[i].END, rt); for (int j = 0; j < size[rt] + size[v[i].END]; j++) g[j] = 0; if (v[i].v == 1) for (int j = 0; j < size[rt]; j++) for (int k = 0; k <= size[v[i].END]; k++) { long long tmp = f[rt][j] * Sum[v[i].END][k - 1] % MOD; long long rmp = C[j + k][j] * C[size[rt] + size[v[i].END] - k - j - 1][size[v[i].END] - k] % MOD; (g[j + k] += tmp * rmp % MOD) %= MOD; } else for (int j = 0; j < size[rt]; j++) for (int k = 0; k <= size[v[i].END]; k++) { long long tmp = f[rt][size[rt] - j - 1] * (Sum[v[i].END][size[v[i].END] - 1] - Sum[v[i].END][size[v[i].END] - k - 1] + MOD) % MOD; long long rmp = C[j + k][j] % MOD * C[size[v[i].END] + size[rt] - k - j - 1][size[v[i].END] - k] % MOD; (g[size[rt] + size[v[i].END] - j - k - 1] += tmp * rmp % MOD) %= MOD; } size[rt] += size[v[i].END]; for (int j = 0; j < size[rt]; j++) f[rt][j] = g[j]; } Sum[rt][0] = f[rt][0]; for (int i = 1; i < size[rt]; i++) Sum[rt][i] = (Sum[rt][i - 1] + f[rt][i]) % MOD;}int main(){ int t = read(); C[0][0] = 1; for (int i = 1; i <= 1000; i++) { C[i][0] = 1; for (int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD; } while (t--) { memset (size, 0, sizeof (size)); memset (Sum, 0, sizeof (Sum)); memset (f, 0, sizeof (f)); memset (first, -1, sizeof (first)); p = 0; int n = read(), a, b; char c[3]; for (int i = 1; i < n; i++) { scanf ("%d%s%d", &a, c, &b); a++, b++; if (c[0] == '>') add(a, b, 1), add(b, a, -1); else add(a, b, -1), add(b, a, 1); } dfs(1, 0); printf ("%lld\n", Sum[1][n - 1]); } // while (1);}
BZOJ 3167 [Heoi2013] Sao
https://www.nekomio.com/posts/117/