[NOI2007]生成树计数
Description
最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现:
·n个结点的环的生成树个数为n。
·n个结点的完全图的生成树个数为n^(n-2)。这两个发现让小栋欣喜若狂,由此更加坚定了他继续计算生成树个数的
想法,他要计算出各种各样图的生成树数目。一天,小栋和同学聚会,大家围坐在一张大圆桌周围。小栋看了看,
马上想到了生成树问题。如果把每个同学看成一个结点,邻座(结点间距离为1)的同学间连一条边,就变成了一
个环。可是,小栋对环的计数已经十分娴熟且不再感兴趣。于是,小栋又把图变了一下:不仅把邻座的同学之间连
一条边,还把相隔一个座位(结点间距离为2)的同学之间也连一条边,将结点间有边直接相连的这两种情况统称
为有边相连,如图1所示。
小栋以前没有计算过这类图的生成树个数,但是,他想起了老师讲过的计算任意图的生成树个数的一种通用方法:
构造一个n×n的矩阵A={aij},其中
其中di表示结点i的度数。与图1相应的A矩阵如下所示。为了计算图1所对应的生成数的个数,只要去掉矩阵A的最
后一行和最后一列,得到一个(n-1)×(n-1)的矩阵B,计算出矩阵B的行列式的值便可得到图1的生成树的个数所以
生成树的个数为|B|=3528。小栋发现利用通用方法,因计算过于复杂而很难算出来,而且用其他方法也难以找到更
简便的公式进行计算。于是,他将图做了简化,从一个地方将圆桌断开,这样所有的同学形成了一条链,连接距离
为1和距离为2的点。例如八个点的情形如下:
这样生成树的总数就减少了很多。小栋不停的思考,一直到聚会结束,终于找到了一种快捷的方法计算出这个图的
生成树个数。可是,如果把距离为3的点也连起来,小栋就不知道如何快捷计算了。现在,请你帮助小栋计算这类
图的生成树的数目。
Input
包含两个整数k,n,由一个空格分隔。k表示要将所有距离不超过k(含k)的结点连接起来,n表示有n个结点。
Output
输出一个整数,表示生成树的个数。由于答案可能比较大,所以你 只要输出答案除65521 的余数即可。
Sample Input
3 5
Sample Output
75
HINT
因为k很小,所以考虑状态压缩
每一个状态表示i的前k个点各自属于哪个连通块
用一个k位的k进制数表示状态
然而状态很多,用最小表示法
即122与233是等价的
这样状态数不超过52
然后就可以DP
$f[i][S]=\sum_{S_last}f[i-1][S_last]$
显然可以用矩阵快速幂
实现枚举S,找到通过变换能形成的所有状态
具体通过一个二进制数,表示第k+1个点向哪些点连边
用并查集判断,并将新的状态改为最小表示
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 lol cnt,n,Mod=65521,k,id[100001],vis[201],set[201]; 9 lol h[201],v[201]; 10 struct Matrix 11 { 12 lol a[201][201]; 13 Matrix operator * (const Matrix &x) const 14 { 15 Matrix res; 16 memset(res.a,0,sizeof(res.a)); 17 for(int i=1;i<=cnt;++i) 18 for(int j=1;j<=cnt;++j) 19 for(int k=1;k<=cnt;++k) 20 res.a[i][j]=(res.a[i][j]+(a[i][k]*x.a[k][j])%Mod)%Mod; 21 return res; 22 } 23 }ans,Mat; 24 int find(int x) 25 { 26 if (set[x]!=x) set[x]=find(set[x]); 27 return set[x]; 28 } 29 lol pow(lol x,lol y) 30 { 31 lol res=1; 32 while (y) 33 { 34 if (y&1) res=res*x%Mod; 35 x=x*x%Mod; 36 y>>=1; 37 } 38 return res; 39 } 40 Matrix qpow(lol y) 41 {int i; 42 Matrix res; 43 memset(res.a,0,sizeof(res.a)); 44 for (i=1;i<=cnt;i++) 45 res.a[i][i]=1; 46 while (y) 47 { 48 if (y&1) res=res*Mat; 49 Mat=Mat*Mat; 50 y>>=1; 51 } 52 return res; 53 } 54 void dfs(int x,int ed,int S) 55 {int i; 56 if (x==k+1) 57 { 58 id[S]=++cnt; 59 h[cnt]=S; 60 return; 61 } 62 for (i=1;i<=ed;i++) 63 dfs(x+1,ed+(i==ed),(i<<(3*(x-1)))+S); 64 } 65 int get_id() 66 { 67 memset(vis,-1,sizeof(vis)); 68 int cc=0,i; 69 for (i=2;i<=k+1;i++) 70 if (vis[find(i)]==-1) vis[find(i)]=++cc; 71 int x=0; 72 for (i=2;i<=k+1;i++) 73 { 74 x+=(vis[find(i)]<<(3*(i-2))); 75 } 76 return id[x]; 77 } 78 void build_Mat(int now,int add) 79 {int i,j,flag; 80 for (i=0;i<=k+1;i++) 81 set[i]=i; 82 for (i=1;i<=k;i++) 83 { 84 for (j=i+1;j<=k;j++) 85 if ((now>>((i-1)*3)&7)==(now>>((j-1)*3)&7)) 86 { 87 int p=find(i),q=find(j); 88 if (p!=q) set[p]=q; 89 } 90 } 91 for (i=1;i<=k;i++) 92 if (add&(1<<i-1)) 93 { 94 int p=find(i),q=find(k+1); 95 if (p==q) return; 96 set[p]=q; 97 } 98 flag=0; 99 for (i=2;i<=k+1;i++) 100 { 101 if (find(1)==find(i)) {flag=1;break;} 102 } 103 if (!flag) return; 104 Mat.a[id[now]][get_id()]++; 105 } 106 int main() 107 {int i,j; 108 cin>>k>>n; 109 dfs(1,1,0); 110 for (i=1;i<=cnt;i++) 111 { 112 for (j=0;j<(1<<k);j++) 113 { 114 build_Mat(h[i],j); 115 } 116 } 117 for (i=1;i<=cnt;i++) 118 { 119 memset(v,0,sizeof(v)); 120 int x=h[i]; 121 int as=1; 122 for (j=1;j<=k;j++) 123 { 124 v[x>>(3*(j-1))&7]++; 125 } 126 for (j=1;j<=k;j++) 127 if (v[j]>=2) 128 { 129 as*=pow(v[j],v[j]-2); 130 } 131 ans.a[1][i]=as; 132 } 133 ans=ans*qpow(n-k); 134 cout<<ans.a[1][1]<<endl; 135 }