[BZOJ1494][NOI2007]生成树计数 状压dp 并查集
2017-12-06 13:42
309 查看
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所示。
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 }
View Code
相关文章推荐
- bzoj1494【Noi2007】生成树计数
- BZOJ 1494 NOI2007 生成树计数 状压DP+矩阵乘法
- BZOJ 1494 [NOI2007]生成树计数 矩阵乘法+DP
- BZOJ 1494: [NOI2007]生成树计数
- bzoj1494: [NOI2007]生成树计数
- bzoj1494 生成树计数(状压dp+生成树+矩阵倍增)
- 【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数
- 【NOI2007/BZOJ1494】生成树计数 插头DP
- [BZOJ1016][JSOI2008]最小生成树计数-并查集-状态压缩-最小生成树
- bzoj 1494: [NOI2007]生成树计数
- 【BZOJ 1494】【NOI 2007】生成树计数
- BZOJ 1494 生成树计数(生成树计数-矩阵)
- [BZOJ 1494][NOI 2007]生成树计数(状压DP+最小表示法+矩阵乘法)
- BZOJ 1016 JSOI 2008 最小生成树计数 Kruskal+搜索
- bzoj 1016 最小生成树计数
- bzoj 1002: [FJOI2007]轮状病毒(生成树计数,高精度)
- BZOJ 1002 轮状病毒(生成树计数+高精度)
- 【BZOJ1016】【JSOI2008】最小生成树计数 & 【BZOJ1543】生成树计数 (kruskal+matrix_tree定理)
- (最小生成树的计数) bzoj 1060
- 【最小生成树】BZOJ1016: [JSOI2008]最小生成树计数