1 条题解

  • 0
    @ 2025-8-24 21:46:51

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar xyz32768
    “各方面相差太远”

    搬运于2025-08-24 21:46:51,当前版本为作者最后更新于2018-03-17 21:13:02,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    先用并查集将所有用等号连接的点缩成一个。然后看到题目中有一个很重要的条件:

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

    缩点后建树,对于每个ii,如果KiK_i存在,就将KiK_i作为ii的父节点。建树后有可能是一棵森林,所以新建一个新的节点n+1n+1连接森林里每棵树的根节点,形成一棵树,n+1n+1为根。

    然后树形DP,f[u]f[u]表示uu的子树内的方案数。

    但对于uu的两个不同子节点vvwwvvww的子树内可能存在两个点质量相等,所以还需要加一维:

    f[u][i]f[u][i]表示uu的子树里,分成ii段(也就是共有i1i-1个小于号把质量序列分成了ii个部分,每个部分里的图片质量相等)的方案数,然后做一次树形背包DP(当前枚举到了uu的子节点vvff'表示枚举到子节点vv之前的DP值):

    $$f[u][i]=\sum_{j,k}f'[u][j]\times f[v][k]\times ORZ $$

    ORZORZ表示jj段和kk段合并成ii段的方案数。

    如何求ORZORZ呢?

    f[u]f[u]的质量序列为AAf[u]f'[u]的质量序列为BBf[v]f[v]的质量序列为CC

    AA中的每一段可以只包含BB中的一段,可以只包含CC中的一段,也可以有BBCC中各一段合并而成,但不能为空。特殊地,AA的第一段只能包含节点uu

    相当于先枚举BB中的j1j-1段在AA中放的位置,方案数为Ci1j1C_{i-1}^{j-1},然后把CC中的iji-j段放到AA中剩下的位置,使每一段都不为空。现在CC中还剩下ki+jk-i+j个段,他们需要与BB中的段合并,方案数Cj1ki+jC_{j-1}^{k-i+j}

    所以:

    ORZ=Ci1j1×Cj1ki+jORZ=C_{i-1}^{j-1}\times C_{j-1}^{k-i+j}

    最后答案if[n+1][i]\sum_{i}f[n+1][i]

    复杂度:f[u][i]f[u][i]第二维的上界只有uu的子树大小,枚举ii相当于枚举ii的子树内的点。所以看上去是O(n4)O(n^4)的,实际上每对点都只在lca处被计算贡献了O(n)O(n)次,复杂度O(n3)O(n^3)

    代码:

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    inline int read() {
    	int res = 0; bool bo = 0; char c;
    	while (((c = getchar()) < '0' || c > '9') && c != '-');
    	if (c == '-') bo = 1; else res = c - 48;
    	while ((c = getchar()) >= '0' && c <= '9')
    		res = (res << 3) + (res << 1) + (c - 48);
    	return bo ? ~res + 1 : res;
    }
    inline char get() {
    	char c; while ((c = getchar()) != '<' && c != '='); return c;
    }
    const int N = 135, M = 265, ZZQ = 1e9 + 7;
    int n, m, X[N], Y[N], fa[N], ecnt, nxt[M], adj[N], go[M], in[N], cnt[N],
    f[N][N], sze[N], C[N][N], g[N];
    bool eq[N], its[N];
    void init() {
    	int i, j; for (i = 0; i <= 120; i++) C[i][0] = 1;
    	for (i = 1; i <= 120; i++) for (j = 1; j <= i; j++)
    		C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % ZZQ;
    }
    void add_edge(int u, int v) {
    	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
    	nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
    }
    int cx(int x) {
    	if (fa[x] != x) fa[x] = cx(fa[x]);
    	return fa[x];
    }
    bool zm(int x, int y) {
    	int ix = cx(x), iy = cx(y);
    	if (ix != iy) fa[iy] = ix;
    	else return 1;
    	return 0;
    }
    void dfs(int u, int fu) {
    	int i, j, k; sze[u] = f[u][1] = 1;
    	for (int e = adj[u], v; e; e = nxt[e]) {
    		if ((v = go[e]) == fu) continue; dfs(v, u);
    		for (i = 1; i <= n; i++) g[i] = 0;
    		for (i = 1; i <= sze[u] + sze[v]; i++) for (j = 1; j <= sze[u]; j++)
    		for (k = 1; k <= sze[v]; k++) {
    			int x = k - i + j; if (x < 0) continue;
    			(g[i] += 1ll * f[u][j] * f[v][k] % ZZQ *
    				C[i - 1][j - 1] % ZZQ * C[j - 1][x] % ZZQ) %= ZZQ;
    		}
    		for (i = 1; i <= sze[u] + sze[v]; i++) f[u][i] = g[i];
    		sze[u] += sze[v]; 
    	}
    }
    int main() {
    	int i; n = read(); m = read(); init();
    	for (i = 1; i <= n; i++) fa[i] = i;
    	for (i = 1; i <= m; i++) X[i] = read(),
    		eq[i] = get() == '=', Y[i] = read();
    	for (i = 1; i <= m; i++) if (eq[i]) zm(X[i], Y[i]);
    	for (i = 1; i <= n; i++) its[in[i] = cx(i)] = 1;
    	for (i = 1; i <= n; i++) fa[i] = i;
    	for (i = 1; i <= m; i++)
    		if (!eq[i]) {
    			add_edge(in[X[i]], in[Y[i]]); cnt[in[Y[i]]]++;
    			if (zm(in[X[i]], in[Y[i]])) return printf("0\n"), 0;
    		}
    	for (i = 1; i <= n; i++) if (its[i] && !cnt[i]) add_edge(n + 1, i);
    	int ans = 0; dfs(n + 1, 0); for (i = 1; i <= sze[n + 1]; i++)
    		ans = (ans + f[n + 1][i]) % ZZQ; cout << ans << endl;
    	return 0;
    }
    
    • 1

    信息

    ID
    2313
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者