目录
题目来源: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;
- }
- }