bzoj1494: [NOI2007]生成樹計數
阿新 • • 發佈:2019-02-18
傳送門
將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);
}