1. 程式人生 > >[BZOJ1494]生成樹計數

[BZOJ1494]生成樹計數

cto operator 個數 最後一行 判斷 state for break desc

[BZOJ1494] [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

試題分析

發現k很小,所以可以用最小表示法表示前k個點加上k+1個點的連通性,然後因為m比較大所以需要矩陣快速冪。
代碼比較挫,在此理一下思路:

  • 把所有最小表示法預處理,並算出每個聯通情況的總數。
  • 考慮每一個狀態加上一個點枚舉連邊情況,並判斷:無環,第一個點(會被刪掉的)是否已經與後面還會考慮的某一個聯通。
  • 加入連通性的時候轉化為最小表示法。
  • 矩陣快速冪。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
 
using namespace std;
#define LL long long
 
inline LL read(){
    LL x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const LL INF = 2147483600;
const LL MAXN = 100010;
const LL Mod = 65521;
 
LL N,M; 
 
struct Mat{
    LL x,y; LL a[101][101];
    inline void init(LL rr,LL cc){x=rr; y=cc; memset(a,0,sizeof(a));} 
}A,Ans; LL Pw[11]; LL sta[101],top;
Mat operator * (Mat A,Mat B){
    Mat C; C.init(A.x,B.y);
    for(LL i=1;i<=A.x;i++){
        for(LL j=1;j<=B.y;j++){
            for(LL k=1;k<=A.y;k++)
                C.a[i][j]+=A.a[i][k]*B.a[k][j]%Mod,C.a[i][j]%=Mod;
        }
    } return C;
}
Mat operator ^ (Mat A,LL P){
    Mat B; B.init(A.x,A.y); for(LL i=0;i<=A.x;i++) B.a[i][i]=1;
    for(; P ; P>>=1,A=A*A) if(P&1) B=A*B; return B;
}   
inline LL Pow(LL A,LL B){
    LL res = 1; for(; B ; B>>=1,A=A*A%Mod) if(B&1) res=res*A%Mod; return res;
} LL Pos[MAXN+1];
inline void Get_state(LL k){
    for(LL i=0;i<=top;i++) sta[i]=0;
    top=0; while(k){
        sta[++top]=k%10,k/=10;
    } while(top<N) sta[top+1]=0,++top; reverse(sta+1,sta+N+1); return ;
} LL cnt; LL state[MAXN+1][11]; LL vis[13];
inline void Add(LL k){
    Get_state(k); memset(vis,0,sizeof(vis)); LL now=0;
    for(LL i=1;i<=N;i++){
        if(!vis[sta[i]]) ++now; vis[sta[i]]++;
        if(now-1<sta[i]) return ;
    } ++cnt; LL res=1; Pos[k]=cnt;
    for(LL i=0;i<=N;i++){
        state[cnt][i]=sta[i];
        if(vis[i]>1) res=res*Pow(vis[i],vis[i]-2)%Mod;
    } Ans.a[1][cnt]=res;return ;
} bool ald[MAXN+1]; LL fa[MAXN+1];
LL find(LL x){
    return (x==fa[x]?x:(fa[x]=find(fa[x])));
}
inline LL GS(){
    for(LL i=0;i<10;i++) vis[i]=-1; LL now=0,id=0;
    for(LL i=2;i<=N+1;i++){
        LL x=find(i); 
        if(vis[x]==-1){
            vis[x]=now; id=id*10+now; ++now;
        } else id=id*10+vis[x];
    } 
    return Pos[id];
}
inline void Add(LL k,LL add_state){
    for(LL i=0;i<=N+1;i++) fa[i]=i;
    for(LL i=1;i<=N;i++){
        for(LL j=i+1;j<=N;j++){
            if(state[k][i]==state[k][j]){
                //cout<<"here\n";
                LL xx=find(i),yy=find(j); 
                //cout<<xx<<" "<<yy<<endl;
                if(xx!=yy) fa[yy]=xx;
            }
        }
    } 
    for(LL i=1;i<=N;i++){
        if(add_state&(1<<(i-1))){
            LL xx=find(N+1),yy=find(i);
            if(xx==yy) return ;
            fa[yy]=xx;
        }
    } bool flag=false;
    for(LL i=2;i<=N+1;i++){
        if(find(1)==find(i)) {flag=true; break;}
    } if(!flag) return ;
    A.a[k][GS()]++;
}
inline void init_state(LL k){
    for(LL i=0;i<(1<<N);i++) Add(k,i); return ;
}
 
int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    N=read(),M=read(); Pw[0]=1;
    for(LL i=1;i<=N;i++) Pw[i]=Pw[i-1]*10;
    for(LL i=0;i<=43210;i++) if(!ald[i%Pw[N]]) Add(i),ald[i%Pw[N]]=true;
    A.init(cnt,cnt); Ans.x=1; Ans.y=cnt;
    for(LL i=1;i<=cnt;i++) init_state(i);
    A=A^(M-N); 
    Ans=Ans*A; printf("%lld\n",Ans.a[1][1]%Mod);
    return 0;
}

[BZOJ1494]生成樹計數