• 【HNOI2015】实验比较


    题面

    题解

    首先将所有相等的用并查集缩点,然后会发现题目有一个很有用的性质:

    对每张图片(i),小D都最多只记住了某一张质量不比(i)差的另一张图片(K_i)

    于是将(K_i)作为(i)的父亲节点,对于(K_i = 0)的点,令(i)的父亲为(n + 1)即可。

    开始树形(dp),设(f[x][i])表示(dp)到点(x),有(i - 1)个小于号的方案数。

    那么我们可以推出一个式子:

    [f[x][i] = sum_{to in son(i), j, k} f'[x][j] imes f[to][k] imes xi ]

    其中(xi)是表示(j)段和(k)段合并成(i)段的方案数。

    我们考虑如何求余项(xi),设(f[x])的质量序列为(A)(f'[x])的质量序列为(B)(f[to])的质量序列为(C)

    (A)的每一段可以只包含(B)的一段或者(C)的一段,也可以同时包含,但不能为空。特殊地,(A)的第一段只能包含节点(x)

    于是(xi)相当于先枚举(B)中的(j - 1)段在(A)中放的位置,方案数为(inom{i - 1}{j - 1}),然后将(C)(i - j)段放在(A)中剩下的位置,使得每一段都不为空。现在(C)中还有(k - i + j)段要与(B)中的段合并,方案数为(inom{j - 1}{k - i + j})

    于是:

    [xi = inom{i - 1}{j - 1} imes inom{j - 1}{k - i + j} ]

    最后答案为(sum_{i = 1}^{mathrm{size}(n + 1)} f[n + 1][i])

    复杂度:因为每个点对只会在LCA处算(mathrm{O}(n))次,于是复杂度是(mathrm{O}(n^3))

    代码

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #define RG register
    
    inline int read()
    {
    	int data = 0, w = 1; char ch = getchar();
    	while(ch != '-' && (!isdigit(ch))) ch = getchar();
    	if(ch == '-') w = -1, ch = getchar();
    	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
    	return data * w;
    }
    
    inline char get()
    {
    	char ch = getchar();
    	while(ch != '<' && ch != '=') ch = getchar();
    	return ch;
    }
    
    const int N(105), Mod(1e9 + 7), M(100);
    struct edge { int next, to; } e[N << 1];
    int n, m, X[N], Y[N], fa[N], eq[N], size[N], e_num;
    int belong[N], cnt[N], head[N], C[N][N], f[N][N];
    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    inline void add_edge(int from, int to)
    {
    	e[++e_num] = (edge) {head[from], to}; head[from] = e_num;
    	e[++e_num] = (edge) {head[to], from}; head[to]   = e_num;
    }
    
    inline int merge(int x, int y)
    {
    	int fx = find(x), fy = find(y);
    	fa[fx] = fy; return fx == fy;
    }
    
    void dfs(int x, int _f)
    {
    	static int g[N]; size[x] = f[x][1] = 1;
    	for(RG int p = head[x]; p; p = e[p].next)
    	{
    		int to = e[p].to; if(to == _f) continue;
    		dfs(to, x); std::fill(g + 1, g + n + 1, 0);
    		for(RG int i = 1; i <= size[x] + size[to]; i++)
    			for(RG int j = 1; j <= size[x]; j++)
    				for(RG int k = 1; k <= size[to]; k++)
    				{
    					int t = k - i + j; if(t < 0) continue;
    					g[i] = (g[i] + 1ll * f[x][j] * f[to][k] % Mod *
    							C[i - 1][j - 1] % Mod * C[j - 1][t]) % Mod;
    				}
    		size[x] += size[to];
    		std::copy(g + 1, g + size[x] + 1, f[x] + 1);
    	}
    }
    
    int main()
    {
    	C[0][0] = 1;
    	for(RG int i = 1; i <= M; i++)
    	{
    		C[i][0] = C[i][i] = 1;
    		for(RG int j = 1; j < i; j++)
    			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod;
    	}
    	n = read(), m = read();
    	for(RG int i = 1; i <= n; i++) fa[i] = i;
    	for(RG int i = 1; i <= m; i++)
    		X[i] = read(), eq[i] = get() == '=', Y[i] = read();
    	for(RG int i = 1; i <= m; i++) if(eq[i]) fa[find(X[i])] = find(Y[i]);
    	for(RG int i = 1; i <= n; i++) belong[i] = find(i);
    	for(RG int i = 1; i <= n; i++) fa[i] = i;
    	for(RG int i = 1; i <= m; i++) if(!eq[i])
    	{
    		add_edge(belong[X[i]], belong[Y[i]]), ++cnt[belong[Y[i]]];
    		if(merge(belong[X[i]], belong[Y[i]])) return puts("0") & 0;
    	}
    	for(RG int i = 1; i <= n; i++)
    		if(belong[i] == i && !cnt[i]) add_edge(n + 1, i);
    	int ans = 0; dfs(n + 1, 0);
    	for(RG int i = 1; i <= size[n + 1]; i++)
    		ans = (ans + f[n + 1][i]) % Mod;
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    PLSQL游标
    SqlHelper助手
    机房重构前奏——三层转七层
    应用运筹管理经济
    C++——宏观把控
    操作系统——宏观把控
    .NET总结一
    深复制与浅复制
    设计模式之结构型
    设计模式之一对多
  • 原文地址:https://www.cnblogs.com/cj-xxz/p/10406971.html
Copyright © 2020-2023  润新知