简单环题解
簡單環
題解:
題目求環的情況
如果我們直接枚舉會有很多重復,為了避免重復,我們枚舉起點,其他的點的序號都必須比起點大,也就是x->y,x一定小于y
dp[i][j]表示的是以i的第一個點作為起點的鏈的數量,j是終點
i是二進制,表示選擇了哪些點
我們先當作鏈來考慮
我們從點j到點t,
前置條件為:i的第t-1位為0,i的第j-1位上為1(也可以是dp[i][j]不為0),j到t有邊
現在我們就構造了一個鏈,中間為t,鏈上的點為i中為1的部分,現在如何考慮環?我們將鏈首位相接即為環,也就是我們找到i的二進制下從低到高出現1的第一個位置z,然后與t相連,當然z和t必須可連才行
統計答案時要除以2,因為每個環都統計了順時針和逆時針兩種情況
詳細看代碼
代碼:
#include<iostream> #include<stack> #include<set> #include<cstring> #include<cmath> #include<stdlib.h> #include<cstdio> #include<queue> #include<algorithm> #include<string> #include<vector> #include <queue> #include <bitset> #include <limits.h> using namespace std; typedef long long ll; const ll mo=998244353; int dp[(1<<20)+100][21]; int e[21][21]={0}; ll kk[50]; ll kpow(ll a,ll b) {ll ans=1;while(b){if(b&1) ans=(ans*a)%mo;a=(a*a)%mo;b>>=1LL;}return ans; } inline int ffs(int x) {for(int i=0;i<30;i++){if(x&(1<<i)) return i+1;}return 0; } inline int popcount(int x) { int num=0;for(int i=0;i<=20;i++){if(x&(1<<i)) num++;}return num; } int main() {ll inv2=kpow(2LL,mo-2);int n,m,k;scanf("%d%d%d",&n,&m,&k);int x,y;for(int i=1;i<=m;i++){scanf("%d%d",&x,&y);e[x][y]=1;e[y][x]=1;}for(int j=1;j<=n;j++){dp[0|(1<<(j-1))][j]=1;}for(int i=1;i<=(1<<n)-1;i++){int x=ffs(i);//從低到高出現1的第一個位置 for(int j=1;j<=n;j++) if(dp[i][j]){for( int t=x+1;t<=n;t++){ if((i&(1<<(t-1)))||e[j][t]==0) continue;dp[i|(1<<(t-1))][t]=(dp[i|(1<<(t-1))][t]+dp[i][j])%mo;}int y=__builtin_popcount(i);//計算里面有多少個1 if(e[j][x]&&y>2) kk[y%k]=(kk[y%k]+dp[i][j])%mo;}}for(int i=0;i<k;i++)kk[i]=kk[i]*inv2%mo;for(int i=0;i<k;i++)printf("%lld\n",kk[i]);return 0; }總結
- 上一篇: 玉米油的功效与作用、禁忌和食用方法
- 下一篇: Most Powerful