• [BZOJ1494]生成树计数


    [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
    ";
                    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
    ",Ans.a[1][1]%Mod);
        return 0;
    }
    
  • 相关阅读:
    NSPrediccate 查询
    集合 不可变集合
    集合 不可变
    考核题 7
    考核题 6
    考核题 4
    练习题12
    练习题3
    iOS 实现在string任意位置添加新的表情
    在 ZBarSDK 中使用Block回调传值 Block在扫描成功后 变为空
  • 原文地址:https://www.cnblogs.com/wxjor/p/9451798.html
Copyright © 2020-2023  润新知