[BZOJ1494][NOI2007]生成树计数 状压dp 并查集
1494: [NOI2007]生成树计数
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 793 Solved: 451
[Submit][Status][Discuss]
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
Source
观察到k很小,n很大。
考虑状压dp,矩阵快速幂优化。
但是我们需要对状态进行离散化,还需有一些小技巧,可以看程序。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #define maxn 150 8 #define mod 65521 9 using namespace std; 10 int size[10]={1,1,1,3,16,125}; 11 long long n,k;int cnt; 12 struct data { 13 long long mat[maxn+1][maxn+1]; 14 data() {memset(mat,0,sizeof(mat));} 15 data operator *(const data t1) { 16 data tp; 17 for(int i=0;i<=cnt;i++) 18 for(int j=0;j<=cnt;j++) 19 for(int k=0;k<=cnt;k++) tp.mat[i][j]+=mat[i][k]*t1.mat[k][j],tp.mat[i][j]%=mod; 20 return tp; 21 } 22 }A,B; 23 int hash[20000],sta[20000]; 24 int fa[20000]; 25 int find(int x){return fa[x]==x?fa[x]:fa[x]=find(fa[x]);} 26 void prepare(int pos,int now,int ma) { 27 if(pos==k+1) { 28 hash[now]=cnt++; 29 sta[cnt-1]=now; 30 return; 31 } 32 for(int i=1;i<=ma;i++) prepare(pos+1,now+(i<<(3*(pos-1))),ma+(i==ma)); 33 } 34 int get() { 35 int h[200]; 36 memset(h,-1,sizeof(h)); 37 int re=0; 38 int cc=0; 39 for(int i=2;i<=k+1;i++) { 40 if(h[find(i)]==-1) h[find(i)]=++cc; 41 } 42 for(int i=2;i<=k+1;i++) { 43 int now=h[find(i)]; 44 re+=(now<<(3*(i-2))); 45 } 46 return hash[re]; 47 } 48 void build(int x,int add) { 49 int now=sta[x]; 50 for(int i=1;i<=k+1;i++) fa[i]=i; 51 for(int i=1;i<=k;i++) { 52 for(int j=i+1;j<=k;j++) { 53 if(((now>>((i-1)*3))&7)==((now>>((j-1)*3))&7)) { 54 int f1=find(i),f2=find(j); 55 if(f1!=f2) fa[f1]=f2; 56 } 57 } 58 } 59 for(int i=1;i<=k;i++) { 60 if(add&(1<<(i-1))) { 61 int f1=find(i),f2=find(k+1); 62 if(f1==f2) return; 63 fa[f1]=f2; 64 } 65 } 66 bool flag=0; 67 for(int i=2;i<=k+1;i++) { 68 if(find(1)==find(i)) {flag=1;break;} 69 } 70 if(!flag) return; 71 A.mat[get()][x]++; 72 } 73 data pow(data x,long long p) { 74 data ans; 75 for(int i=0;i<=maxn;i++) ans.mat[i][i]=1; 76 while(p) { 77 if(p&1) ans=ans*x; 78 x=x*x; 79 p>>=1; 80 81 } 82 return ans; 83 } 84 int main() { 85 for(int i=0;i<=maxn;i++) B.mat[i][0]=1; 86 scanf("%lld%lld",&k,&n); 87 prepare(1,0,1); 88 for(int i=0;i<cnt;i++) 89 for(int j=0;j<(1<<k);j++) build(i,j); 90 /*for(int i=0;i<cnt;i++) { 91 for(int j=0;j<=k;j++) cout<<A.mat[i][j]<<' '; 92 cout<<endl; 93 }*/ 94 for(int i=0;i<cnt;i++) { 95 int now=sta[i]; 96 int tmp[10]={}; 97 for(int j=1;j<=k;j++) tmp[now>>((j-1)*3)&7]++; 98 for(int j=1;j<=k;j++) B.mat[i][0]*=size[tmp[j]]; 99 } 100 101 A=pow(A,n-k); 102 A=A*B; 103 printf("%lld",A.mat[0][0]%mod); 104 }
O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~