[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 }
posted @ 2018-03-19 07:29  Z-Y-Y-S  阅读(500)  评论(0编辑  收藏  举报