先转换成求简单路径(一条链)的数目,然后每条路径一个状态。
为了避免重复,枚举起点,其他的点的序号都需要比起点大。
dp[s][j],s表示点集,j表示终点,s点集中最小的序号作为起点。
状态转移:dp[ s | i ][ i ] += dp[ s ][ j ] (if j->i有边 && s不包含i && i的序号大于起点)
在递推过程中,会存在许多无用的空状态,跳过就好(不然会超时)
最后再计算环的数目,注意一个简单环可以表示为两个上述简单路径
AC代码:
1 #include<iostream> 2 #include<stack> 3 #include<set> 4 #include<cstring> 5 #include<cmath> 6 #include<stdlib.h> 7 #include<cstdio> 8 #include<queue> 9 #include<algorithm> 10 #include<string> 11 #include<vector> 12 #include <queue> 13 #include <bitset> 14 #include <limits.h> 15 using namespace std; 16 typedef long long ll; 17 const ll mo=998244353; 18 int dp[(1<<20)+100][21]; 19 int e[21][21]={0}; 20 ll kk[50]; 21 ll kpow(ll a,ll b) { 22 ll ans=1; 23 while(b){ 24 if(b&1) ans=(ans*a)%mo; 25 a=(a*a)%mo; 26 b>>=1LL; 27 } 28 return ans; 29 } 30 inline int ffs(int x) 31 { 32 for(int i=0;i<30;i++) 33 { 34 if(x&(1<<i)) return i+1; 35 } 36 return 0; 37 } 38 inline int popcount(int x) 39 { int num=0; 40 for(int i=0;i<=20;i++) 41 { 42 if(x&(1<<i)) num++; 43 } 44 return num; 45 } 46 int main() 47 { 48 ll inv2=kpow(2LL,mo-2); 49 50 int n,m,k; 51 scanf("%d%d%d",&n,&m,&k); 52 int x,y; 53 for(int i=1;i<=m;i++) 54 { 55 scanf("%d%d",&x,&y); 56 e[x][y]=1; 57 e[y][x]=1; 58 } 59 60 for(int j=1;j<=n;j++) 61 { 62 dp[0|(1<<(j-1))][j]=1; 63 } 64 for(int i=1;i<=(1<<n)-1;i++) 65 { 66 int x=ffs(i); 67 for(int j=1;j<=n;j++) if(dp[i][j]) 68 { 69 70 for( int t=x+1;t<=n;t++) 71 { if((i&(1<<(t-1)))||e[j][t]==0) continue; 72 73 74 dp[i|(1<<(t-1))][t]=(dp[i|(1<<(t-1))][t]+dp[i][j])%mo; 75 } 76 int y=__builtin_popcount(i); 77 if(e[j][x]&&y>2) kk[y%k]=(kk[y%k]+dp[i][j])%mo; 78 } 79 } 80 81 for(int i=0;i<k;i++) 82 kk[i]=kk[i]*inv2%mo; 83 84 for(int i=0;i<k;i++) 85 printf("%lld\n",kk[i]); 86 return 0; 87 }