1. 程式人生 > >bzoj1494: [NOI2007]生成樹計數

bzoj1494: [NOI2007]生成樹計數

傳送門
將k個點的連通性用最小表示法壓成狀態,那麼最多有52種狀態
最小表示法中,f[i]表示最小的與其聯通的點編號。
計算出每個狀態的生成樹個數,作為初始行向量A
對於每種狀態考慮新加入一個點並向這k個點連邊,每種連法可以轉移到哪些狀態,得到轉移矩陣B
那麼答案就是A∗Bn[所有點都連通的狀態]
就是程式碼炒雞長。

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mo 65521 #define uint unsigned int #define ll long long using namespace std; int k,tot,pos[33333]; ll n; uint A[60],ans; struct mat{ uint a[60][60]; mat(int fl){ memset(a,0,sizeof(a)); for (int i=1;i<=tot;i++) a[i][i]=fl; } uint* operator [](int x){ return
a[x]; } friend mat& operator *=(mat &x,mat y){ mat z(0); for (int i=1;i<=tot;i++) for (int j=1;j<=tot;j++) for (int k=1;k<=tot;k++) z[i][j]=(z[i][j]+x[i][k]*y[k][j])%mo; return x=z; } }a(0); mat power(mat x,ll y){ mat s(1
); for (;y;y/=2,x*=x) if (y&1) s*=x; return s; } void express(int a[],int x){ for (int i=0;i<k;i++) a[i]=(x>>i*3)&7; } int impress(int a[]){ int s=0; for (int i=0;i<k;i++) s|=a[i]<<i*3; return s; } void stdsize(int a[]){ static int b[10]; int cnt=0; memset(b,0,sizeof(b)); for (int i=0;i<k;i++) if (!b[a[i]]) b[a[i]]=++cnt; for (int i=0;i<k;i++) a[i]=b[a[i]]; } bool ok(int x){ static int a[10]; int now=1; express(a,x); for (int i=0;i<k;i++) if (!a[i]||a[i]>now) return 0; else if (a[i]==now) now++; return 1; } void calc1(int x){ static int A[10],b[10],cnt[10]; int lim=0; express(A,x); memset(cnt,0,sizeof(cnt)); for (int i=0;i<k;i++){ cnt[A[i]]++; lim=max(lim,A[i]); } for (int _x=0;_x<1<<lim;_x++){ if (cnt[1]==1&&_x%2==0) continue; int tmp=1; memcpy(b,A,sizeof(A)); b[k]=9; for (int i=1;i<=lim;i++) if (_x&(1<<i-1)) tmp*=cnt[i]; for (int i=0;i<k;i++) if (_x&(1<<A[i]-1)) b[i]=9; stdsize(b+1); a[pos[x]][pos[impress(b+1)]]=tmp; } } int power(int x,int y){ int s=1; for (;y>0;y--) s*=x; return s; } void calc2(uint A[],int x){ static int a[10],cnt[10]; int s=1; express(a,x); memset(cnt,0,sizeof(cnt)); for (int i=0;i<k;i++) cnt[a[i]]++; for (int i=1;cnt[i];i++) s*=power(cnt[i],cnt[i]-2); A[pos[x]]=s; } int main(){ scanf("%d%lld",&k,&n); if (k>n) k=n; for (int i=0;i<1<<k*3;i++) if (ok(i)) pos[i]=++tot; for (int i=0;i<1<<k*3;i++) if (pos[i]){ calc1(i); calc2(A,i); } mat B=power(a,n-k); for (int i=1;i<=tot;i++) ans=(ans+A[i]*B[i][1])%mo; printf("%u",ans); }