W17-C-简单环(状压dp)

 

先转换成求简单路径(一条链)的数目,然后每条路径一个状态。

为了避免重复,枚举起点,其他的点的序号都需要比起点大。

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 }

 

转载于:https://www.cnblogs.com/lnu161403214/p/9186079.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值