目录
题目来源:https://ac.nowcoder.com/acm/contest/25022/1022
简单环
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给定一张n个点m条边的无向图,求出图中所有简单环的数量。(简单环:简单环又称简单回路,图的顶点序列中,除了第一个顶点和最后一个顶点相同外,其余顶点不重复出现的回路叫简单回路。或者说,若通路或回路不重复地包含相同的边,则它是简单的)
输入描述:
第一行三个数n m k (k在输出描述中提到) 接下来m行每行两个数u,v表示u到v之间存在一条边(无重边)
输出描述:
输出k行每行1个整数 第i个数表示长度%k==i-1的简单环的数量(对998244353取模) (只记录长度大于2的简单环的数量)
示例1
输入
复制
4 6 3 1 2 2 3 3 4 4 1 2 4 1 3
输出
复制
4 3 0
备注:
n<=20 m<=n*(n-1)/2 k<=n
思路解析:
简单环的定义:
(简单环:简单环又称简单回路,图的顶点序列中,除了第一个顶点和最后一个顶点相同外,其余顶点不重复出现的回路叫简单回路。或者说,若通路或回路不重复地包含相同的边,则它是简单的)
因为他是一个n个点m条边的无向图,要求求简单环的数量,
因为他没有重边,所以他从i节点到j节点的路径长度为一的路径只有一个,所以我们可以记录上一次状态的终点,然后从他转移到当前状态的终点上。dp[i][j]表示在状态i下,j为状态终点的路径方案数。
dp[ s | (1 << (j - 1))][j] = (dp[ s | (1 << (j - 1))][j] + dp[ s | (1 << (j - 1))][i]) % mod;
然后如果当前状态的终点可以走到起点的话他就构成了一个环。这里又因为一个环起点的位置并不影响这个环的结构,所以我们把一个状态中最小(最右)的节点作为起点,并且之后经过的起点一定要比起点大,这样可以避免重复计算。但他一定会重复计算两次这个无法避免。
如 中间状态 2 - 3 - 5,我们可以让起点连向2,也可以让起点连向5,所以统计完答案后,一定要除以2的逆元。
代码实现
import java.util.Scanner;
import java.util.Vector;
/**
* @ProjectName: study3
* @FileName: Ex22
* @author:HWJ
* @Data: 2023/11/8 20:38
*/
public class Ex22 {
static int mod = 998244353;
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int m = input.nextInt();
int k = input.nextInt();
int[] ans = new int[k];
int[][] dp = new int[1 << n][n + 1];
int[][] map = new int[n + 1][n + 1];
int[] num = new int[1 << n];
for (int i = 0; i < 1 << n; i++) {
num[i] = calc(i);
}
for (int i = 0; i < m; i++) {
int a = input.nextInt();
int b = input.nextInt();
map[a][b] = 1;
map[b][a] = 1;
}
for (int i = 1; i <= n; i++) {
dp[1 << (i - 1)][i] = 1;
}
for (int s = 1; s < (1 << n); s++) {
int st = -1;
for (int i = 0; i < n; i++) {
if ((s & (1 << i)) != 0){
st = i + 1; // 在这个状态下 以这个节点作为状态。
break;
}
}
for (int i = st; i <= n; i++) {
if ((s & (1 << (i - 1))) != 0){
if (map[i][st] == 1 && num[s] > 2){
// System.out.println(s + " " + num[s] % k);
ans[num[s] % k] = (ans[num[s] % k] + dp[s][i]) % mod;
}
for (int j = st + 1; j <= n; j++) {
if (map[i][j] == 1 && (s & (1 << (j - 1))) == 0){
int a = s | (1 << (j - 1));
dp[a][j] = (dp[a][j] + dp[s][i]) % mod;
}
}
}
}
}
for (int i = 0; i < k; i++) {
System.out.println((ans[i] * quick(2)) % mod);
}
}
public static int calc(int x){
int ans = 0;
while(x > 0){
if ((x & 1) == 1) ans++;
x >>= 1;
}
return ans;
}
public static long quick(long x){
int a = mod - 2;
long ans = 1;
for (; a > 0; a >>= 1, x = x * x % mod) {
if ((a & 1) == 1) ans = (ans * x) % mod;
}
return ans;
}
}