题意
一个 n n 个点的树,点只能向 [i−k,i−1] [ i − k , i − 1 ] 内的点连边,求有标号生成树的个数
n≤1015,k≤5 n ≤ 10 15 , k ≤ 5
题解
题面误导向系列,显然不能像他说的那样做吧
因为发现 k k 特别小,所以可以考虑用状压表示和前 k−1 k − 1 个点的联通情况
考虑 DP,f[i][j] D P , f [ i ] [ j ] 表示前 i i 个点,这些点的连通性是 j j 的方案数
考虑到有些状态本质上是一样的()
所以考虑用最小表示法来存状态
如 k=3 k = 3 时有 5 5 种状态
数字表示第 i i 属于第几个集合
如表示 {1,3},{2} { 1 , 3 } , { 2 } 即 1,3 1 , 3 联通
通过爆搜搜出所有满足最小表示法的状态 (k=5 ( k = 5 时有 52 52 种 ) )
这里稍微解释一下,就是假设当前点所在的最大的集合编号是
那么下一个点最多只能在第 i+1 i + 1 个集合,否则就是非法的,不然就更新最大值
这个爆搜完全可以直接每个点开一个数组去判断,代码里用压位二进制写的然后一个最小表示法实际上对应很多种树 (n ( n 个点的有标号无根树个数的 nn−2) n n − 2 )
e.g. 11122 e . g . 11122 表示 {1,2,3},{4,5} { 1 , 2 , 3 } , { 4 , 5 } 对应的树的种数是 33−2×22−2=3 3 3 − 2 × 2 2 − 2 = 3
暴力枚举新加入一个点之后两个状态之间的转移
也就是 2k 2 k 枚举新的点和前面的那些点 ([i−k,i−1]) ( [ i − k , i − 1 ] ) 是否连边
1. 1. 下一状态不能有环
2. 2. 下一状态必须与当前状态所有的点都连通
3. 3. 可以用并查集来判断并求出所有满足条件的转移数
4. 4. 转移的时候把点 i−k−1 i − k − 1 当做 0 0 号点,点当做 k k 号点,会比较好转移一些设表示从 j j 状态转移到状态的方案数
设 Cnt C n t 为状态数,那么有
f[i][j]=∑k=0Cntf[i−1][k]×g[k][j] f [ i ] [ j ] = ∑ k = 0 C n t f [ i − 1 ] [ k ] × g [ k ] [ j ]然后可以发现这就是矩阵乘法的一步,所以把 g g 表示成矩阵然后直接矩阵快速幂就好了
最后把矩阵快幂得到的矩阵乘上这个矩阵的和就是答案了
因为最后的状态必须是 1…1 1 … 1 (所有点都要联通),所以只要算上第一个状态的答案就好了
这个代码可能要认真看几遍才能看得懂
感觉还是比较短的
#include<bits/stdc++.h> #define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i) #define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i) #define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to) #define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;} template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;} using namespace std; const int K=6,S=55,P=65521,T[]={1,1,1,3,16,125}; typedef long long ll; int k,Cnt,ans,sum[K],fa[K],f[S],sta[S],pos[1<<16];ll n; inline int pls(int a,int b){return a+=b,a<P?a:a-P;} struct matrix{ int a[S][S]; matrix(int x=0){memset(a,0,sizeof a);if(x)fp(i,1,Cnt)a[i][i]=1;} inline int*operator[](int x){return a[x];} inline matrix operator*(matrix b){ matrix c; fp(i,1,Cnt)fp(k,1,Cnt)if(a[i][k])fp(j,1,Cnt) c[i][j]=pls(c[i][j],(ll)a[i][k]*b[k][j]%P); return c; } inline matrix operator^(ll b){ matrix x(1),a=*this; for(;b;b>>=1,a=a*a)if(b&1)x=x*a; return x; } }g; inline bool chk(int s){ int tp=1; for(int i=0;i<3*k;tp|=1<<(((s>>i)&7)),i+=3) fp(j,0,((s>>i)&7)-1)if(!(tp&(1<<j)))return 0; return 1; } void dfs(int d,int s){ if(d==k){if(chk(s))sta[++Cnt]=s,pos[s]=Cnt;return;} fp(i,1,k)dfs(d+1,s|(i<<(3*d))); } int gf(int x){return fa[x]==x?x:fa[x]=gf(fa[x]);} inline void Getnx(int i,int s,int nw){ fp(j,0,k)fa[j]=j; fp(j,0,k)fp(l,j+1,k) if(((nw>>(3*j))&7)==((nw>>(3*l))&7))fa[gf(j)]=gf(l); fp(j,0,k)if(s&(1<<j)){ if(gf(j)^gf(k))fa[fa[j]]=k; else return; } int tp=0,nx=0,fg=0; fp(j,1,k)if(gf(0)==gf(j)){fg=1;break;}; if(!fg)return; fp(j,0,k-1)if(!(nx&(7<<(j*3)))){ nx|=++tp<<(j*3); fp(l,j+1,k-1)if(gf(j+1)==gf(l+1)) nx|=tp<<(l*3); } ++g[i][pos[nx]]; } int main(){ #ifndef ONLINE_JUDGE file("s"); #endif scanf("%d%lld",&k,&n);dfs(0,1); fp(i,1,Cnt){ f[i]=1;int nw=sta[i]; memset(sum,0,sizeof sum); fp(j,0,k-1)++sum[(nw>>(j*3))&7]; fp(j,1,k)f[i]*=T[sum[j]]; fp(s,0,(1<<k)-1)Getnx(i,s,nw); }g=g^(n-k); fp(i,1,Cnt)ans=pls(ans,f[i]*g[i][1]%P); printf("%d",ans); return 0; }