4 5 1 2 2 3 3 4 4 1 1 3
5
ACBANG_Magle的题解:
解题思路:
如果本题的要求仅仅只是求这个图的生成树的个数,那么可以使用Kirchhoff矩阵求解:
但是该题中还存在一个问题,就是要求树上存在一个环,那么就需要缩点来实现了。将符合要求的环缩成一个点,最后的结果就是Σ(circle[i] * |C[G]|)。
先在仅剩下求环问题了。由于会缩点,因此,对同样的点的环,我们并不关心其环的形状,我们关心的仅仅是环的数量,因此可以将点进行状压(点的个数只有16个)。有如下定义:
dp[i][j] 代表 i 中状态下呈现链状的最后节点为j的种类
dp[i][j] = Σ dp[k][p](实际中是对dp[k][p]向后进行转移的)
同时保证,i状态下,链的起点为i中编号最小的点。
通过sum[i]数组记录i状态下的方案数。
通过上述步骤就可以解决该题了。
Tips:
由于缩点所产生的重复边是需要保存的。
由于环存在方向性,因此,就算规定了起点,对于一条链也会有2种转移方法,因此,最后结果需要除2。
在本题中与要注意使用逆元实现最后的除2,不能直接去除
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<functional> #include<iostream> #include<cmath> #include<cctype> #include<ctime> using namespace std; #define For(i,n) for(int i=1;i<=n;i++) #define Fork(i,k,n) for(int i=k;i<=n;i++) #define Rep(i,n) for(int i=0;i<n;i++) #define ForD(i,n) for(int i=n;i;i--) #define RepD(i,n) for(int i=n;i>=0;i--) #define Forp(x) for(int p=pre[x];p;p=next[p]) #define Forpiter(x) for(int &p=iter[x];p;p=next[p]) #define Lson (x<<1) #define Rson ((x<<1)+1) #define MEM(a) memset(a,0,sizeof(a)); #define MEMI(a) memset(a,127,sizeof(a)); #define MEMi(a) memset(a,128,sizeof(a)); #define INF (2139062143) #define F (998244353) #define eps (1e-3) #define MAXN (16+10) #define MAXM (16*16+10) typedef __int64 ll; ll mul(ll a,ll b){return (a*b)%F;} ll add(ll a,ll b){return (a+b)%F;} ll sub(ll a,ll b){return (a-b+llabs(a-b)/F*F+F)%F;} void upd(ll &a,ll b){a=(a%F+b%F)%F;} int n,m; ll r2; struct M { int n,m; ll a[MAXN][MAXN]; M(int _n=0){n=m=_n;MEM(a);} M(int _n,int _m){n=_n,m=_m;MEM(a);} void mem (int _n=0){n=m=_n;MEM(a);} void mem (int _n,int _m){n=_n,m=_m;MEM(a);} friend M operator*(M a,M b) { M c; For(k,a.m) For(i,a.n) For(j,b.m) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%F; return c; } void make_I(int _n) { n=m=_n; MEM(a) For(i,n) a[i][i]=1; } // 求行列式 long double mat[MAXN][MAXN],tmp[MAXN]; long double det() { For(i,n) For(j,m) mat[i][j]=a[i][j]; For(i,n) { int pos=i; while (fabs(mat[pos][i])<eps&&pos<n) ++pos; if (fabs(mat[pos][i])<eps) continue; if (pos^i) { copy(mat[pos]+1,mat[pos]+1+m+1,tmp+1); copy(mat[i]+1,mat[i]+1+m+1,mat[pos]+1); copy(tmp+1,tmp+1+m+1,mat[i]+1); } For(j,n) if (i^j) { long double p = mat[j][i]/mat[i][i]; For(k,m) mat[j][k]-=mat[i][k]*p; } } long double ans=1; For(i,n) ans*=mat[i][i]; return ans; } }A,D,C; ll dp[(1<<16)+100][MAXN],sum[(1<<16)+100]; const ll p2[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536}; int pos_1(ll x){ll cnt=1; while ((x&1)^1) cnt++,x>>=1; return cnt; } int all_1(ll x){ll cnt=0; while (x) cnt+=x&1,x>>=1; return cnt;} void solve(int n) { MEM(dp) MEM(sum) For(i,p2[n]-1) { ll cnt=all_1(i),pos=pos_1(i); if (cnt==1) { dp[i][pos]=1; } For(j,n) { if (i&p2[j-1]) { if (cnt>=3&&A.a[j][pos]) upd (sum[i], dp[i][j]); Fork(l,pos+1,n) if ( (p2[l-1]&i)==0 && A.a[j][l] ) upd(dp[i+p2[l-1]][l], dp[i][j] ); } } } } M a; int t[MAXN]; void work() { ll ans=0; For(i,p2[n]-1) { if (sum[i]) { ll cnt=all_1(i); ll p=0; For(j,n) if (i&p2[j-1]) t[j]=n-cnt+1; else t[j]=++p; a.mem(n-cnt); For(j,n) For(l,n) if (t[j]!=t[l]&&A.a[j][l]) { a.a[t[j]][t[j]]++; a.a[t[j]][t[l]]--; } ll t2=(ll)(fabs(a.det())+eps)%F; upd(ans,t2*sum[i]); } } ans=mul(ans,r2); cout<<ans<<endl; } int u[MAXM],v[MAXM]; __int64 pow2(ll x,ll b) { if (b==0) return 1; if (b==1) return x%F; ll p=pow2(x,b/2); p=p*p%F; if (b&1) p=p*x%F; return p; } int main() { // freopen("E.in","r",stdin); r2=pow2(2,F-2); while (cin>>n>>m) { A.mem(n),D.mem(n),C.mem(n); For(i,m) { scanf("%d%d",&u[i],&v[i]); D.a[u[i]][u[i]]++; D.a[v[i]][v[i]]++; A.a[u[i]][v[i]]++; A.a[v[i]][u[i]]++; } solve(n); work(); } return 0; }