传送门
将k个点的连通性用最小表示法压成状态,那么最多有52种状态
最小表示法中,f[i]表示最小的与其联通的点编号。
计算出每个状态的生成树个数,作为初始行向量A
对于每种状态考虑新加入一个点并向这k个点连边,每种连法可以转移到哪些状态,得到转移矩阵B
那么答案就是A∗Bn[所有点都连通的状态]
就是代码炒鸡长。
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mo 65521
#define uint unsigned int
#define ll long long
using namespace std;
int k,tot,pos[33333];
ll n;
uint A[60],ans;
struct mat{
uint a[60][60];
mat(int fl){
memset(a,0,sizeof(a));
for (int i=1;i<=tot;i++)
a[i][i]=fl;
}
uint* operator [](int x){
return a[x];
}
friend mat& operator *=(mat &x,mat y){
mat z(0);
for (int i=1;i<=tot;i++)
for (int j=1;j<=tot;j++)
for (int k=1;k<=tot;k++)
z[i][j]=(z[i][j]+x[i][k]*y[k][j])%mo;
return x=z;
}
}a(0);
mat power(mat x,ll y){
mat s(1);
for (;y;y/=2,x*=x)
if (y&1) s*=x;
return s;
}
void express(int a[],int x){
for (int i=0;i<k;i++)
a[i]=(x>>i*3)&7;
}
int impress(int a[]){
int s=0;
for (int i=0;i<k;i++)
s|=a[i]<<i*3;
return s;
}
void stdsize(int a[]){
static int b[10];
int cnt=0;
memset(b,0,sizeof(b));
for (int i=0;i<k;i++)
if (!b[a[i]]) b[a[i]]=++cnt;
for (int i=0;i<k;i++) a[i]=b[a[i]];
}
bool ok(int x){
static int a[10];
int now=1;
express(a,x);
for (int i=0;i<k;i++)
if (!a[i]||a[i]>now) return 0;
else if (a[i]==now) now++;
return 1;
}
void calc1(int x){
static int A[10],b[10],cnt[10];
int lim=0;
express(A,x);
memset(cnt,0,sizeof(cnt));
for (int i=0;i<k;i++){
cnt[A[i]]++;
lim=max(lim,A[i]);
}
for (int _x=0;_x<1<<lim;_x++){
if (cnt[1]==1&&_x%2==0) continue;
int tmp=1;
memcpy(b,A,sizeof(A));
b[k]=9;
for (int i=1;i<=lim;i++)
if (_x&(1<<i-1)) tmp*=cnt[i];
for (int i=0;i<k;i++)
if (_x&(1<<A[i]-1)) b[i]=9;
stdsize(b+1);
a[pos[x]][pos[impress(b+1)]]=tmp;
}
}
int power(int x,int y){
int s=1;
for (;y>0;y--) s*=x;
return s;
}
void calc2(uint A[],int x){
static int a[10],cnt[10];
int s=1;
express(a,x);
memset(cnt,0,sizeof(cnt));
for (int i=0;i<k;i++) cnt[a[i]]++;
for (int i=1;cnt[i];i++)
s*=power(cnt[i],cnt[i]-2);
A[pos[x]]=s;
}
int main(){
scanf("%d%lld",&k,&n);
if (k>n) k=n;
for (int i=0;i<1<<k*3;i++)
if (ok(i)) pos[i]=++tot;
for (int i=0;i<1<<k*3;i++)
if (pos[i]){
calc1(i);
calc2(A,i);
}
mat B=power(a,n-k);
for (int i=1;i<=tot;i++)
ans=(ans+A[i]*B[i][1])%mo;
printf("%u",ans);
}
1374




被折叠的 条评论
为什么被折叠?



