1 条题解

  • 0
    @ 2025-8-24 22:18:53

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 封禁用户
    None

    搬运于2025-08-24 22:18:53,当前版本为作者最后更新于2023-05-13 15:04:25,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    题目链接

    简要题面:

    有一个无向图(可能不连通),nn 个点,mm 条边。两个不同点的编号之差不大于 KK。任意一个点的度数都为偶数(有无度数的点)。对这个无向图的形态进行计数,模 109+710^9 + 7

    1n,m301 \le n,m \le 301K81 \le K \le 8

    思路:

    状压 DP\texttt{DP},计数。很好想的。这一类题的难点其实在不重不漏。

    DP\texttt{DP} 题都是要看性质的。题面其实提示的很清楚了:“任意一个点的度数都为偶数”。显然,出题人希望你通过点的度数的奇偶来推无向图的形态。所以无脑设这题的状态是 fi,j,kf_{i,j,k} 表示 ii 个点,jj 条边,kk 是每个点度数的奇偶(11 是奇数,00 是偶数)。

    边界呢?因为当 i=1i=1 时是没有意义的,所以 ii 是要从 22 开始的,1f2,0,01 \to f_{2,0,0}

    枚举范围呢?“两个不同点的编号之差不大于 KK”,这句话启示我们让iKi1i-K \sim i-1 号点转移到 ii 号点。

    ii 位是当前结点,所以每一加一条边度数都会改变,所以 k1kk \oplus 1 \to k(状态正着存)。然后改变另一个点的奇偶,即 k(2ic)kk \oplus (2^{i-c}) \to kcc 为与 ii 连边的点)。

    可以发现,这样的转移是有序的,每次都由编号大的结点指向编号小的结点。

    于是有转移方程:

    $f_{i,j,k} = \sum^{i-1}_{c = i-K} {\sum^{2^{K+1}-1}_{a=0}}f_{i,j-1,k \oplus 1 \oplus 2^{i-c}}$

    于是某个大聪明打出了这样的代码:

    signed main() {
    	cin >> n >> m >> k;
    	dp[2][0][0] = 1;
    	for (int i = 2; i <= n; i++) {
    		for (int change = max(1, i - k); change <= i - 1; change++) {
    			for (int j = 1; j <= m; j++) {
    				for (int now_state = 0; now_state < (1 << (k + 1)); now_state++) {
    					dp[i][j][now_state] = (dp[i][j][now_state] + dp[i][j - 1][now_state ^ 1 ^ (1 << i - change)]) % mod;
    				}
    			}
    		}
    	}
    	cout<<dp[n][m][0]<<'\n';
    	return 0;
    }
    

    诶,怎么不对呢?怎么都输出 0 啊。形如这样的情况,说明漏了转移。

    我们观察到,每一次多一个点的情况都要转移过去,这也是没有讨论全面的一个错误。

    添加一个发送式转移至下个阶段:

    $f_{i+1,j,k \times 2^{1}} = \sum ^{m}_{j=0} \sum^{2^K-1}_{k = 0}f_{i,j,k}$

    坑点:位运算要打括号!取模要小心!

    Code:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod = 1e9 + 7;
    const int Data_Range_Of_N = 30 + 5;
    const int Data_Range_Of_K = 10 + 5;
    int n, m, k;
    int dp[Data_Range_Of_N][Data_Range_Of_N][1 << Data_Range_Of_K];
    signed main() {
    	ios::sync_with_stdio(0);
    	cin.tie(0);
    	cout.tie(0);
    	cin >> n >> m >> k;
    	dp[2][0][0] = 1;
    	for (int i = 2; i <= n; i++) {
    		for (int change = max(1, i - k); change <= i - 1; change++) {
    			for (int j = 1; j <= m; j++) {
    				for (int now_state = 0; now_state < (1 << (k + 1)); now_state++) {
    					dp[i][j][now_state] = (dp[i][j][now_state] + dp[i][j - 1][now_state ^ 1 ^ (1 << i - change)]) % mod;
    				}
    			}
    		}
    		for(int j = 0;j<=m;j++) {
    			for(int now_state = 0;now_state < (1 << k);now_state++) {
    				dp[i+1][j][now_state << 1] = (dp[i][j][now_state] + dp[i+1][j][now_state << 1]) % mod;
    			}
    		}
    	}
    	cout<<dp[n][m][0]<<'\n';
    	return 0;
    }
    
    • 1

    信息

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