zoukankan      html  css  js  c++  java
  • [NOI2007]生成树计数

    题意

    一个n个点的树,点i只能向[i−k,i−1]内的点连边,求有标号生成树的个数

    然而做法,好吧我也不知道这是什么做法,所以有些注释大部分注释所有注释我都写在了代码里面,大佬们就自己去看看吧

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define RG register
    #define MOD 65521
    #define MAX 55
    inline ll read()
    {
        RG ll x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    ll n;int K,cnt;
    int p[1<<20],st[MAX];
    struct Matrix
    {
        int s[MAX][MAX];
        void clear(){memset(s,0,sizeof(s));}
        void init(){clear();for(int i=1;i<=cnt;++i)s[i][i]=1;}
    }G;
    Matrix operator*(Matrix a,Matrix b)
    {
        Matrix ret;ret.clear();
        for(int i=1;i<=cnt;++i)
            for(int j=1;j<=cnt;++j)
                for(int k=1;k<=cnt;++k)
                    ret.s[i][j]=(ret.s[i][j]+1ll*a.s[i][k]*b.s[k][j])%MOD;
        return ret;
    }
    Matrix fpow(Matrix a,ll b)
    {
        Matrix s;s.init();
        while(b){if(b&1)s=s*a;a=a*a;b>>=1;}
        return s;
    }
    bool check(int t)//检查一个状态是否合法
    {
        int tmp=1<<1;//因为第一个点一定属于一号联通快,所以先把一号联通快放进去检查
        for(int i=3;i<K+K+K;i+=3)
        {
            for(int j=1;j<((t>>i)&7);++j)//检查比当前编号小的所有编号是否都已经出现过
                if(!(tmp&(1<<j)))return false;
            tmp|=1<<((t>>i)&7);//将当前编号也给放进来
        }
        return true;
    }
    void dfs(int x,int t)//暴力找出所有状态,每个编号利用3个二进制位存
    {
        if(x==K){if(check(t))p[t]=++cnt,st[cnt]=t;return;}
        for(int i=1;i<=K;++i)dfs(x+1,t|(i<<(x+x+x)));
    }
    int fa[MAX],a[MAX];
    int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
    int f[MAX],g[MAX][MAX];
    int main()
    {
        K=read();n=read();dfs(1,1);
        for(int i=1;i<=cnt;++i)
        {
            f[i]=1;memset(a,0,sizeof(a));
            for(int j=0;j<K;++j)++a[(st[i]>>(j*3))&7];
            for(int j=1;j<=K;++j)
                if(a[j]==3)f[i]=3;
                else if(a[j]==4)f[i]=16;
                else if(a[j]==5)f[i]=125;
            int t=st[i];
            for(int s=0;s<(1<<K);++s)//暴力枚举当前点对于前面几个点的连边状态
            {
                for(int j=0;j<=K;++j)fa[j]=j;
                for(int j=0;j<K;++j)//利用并查集维护联通性
                    for(int k=j+1;k<K;++k)
                        if(((t>>(3*j))&7)==((t>>(3*k))&7))
                            fa[getf(j)]=getf(k);
                bool cir=false;
                for(int j=0;j<K;++j)//检查当前点的连边
                    if(s&(1<<j))
                    {
                        if(getf(K)==getf(j)){cir=true;break;}//出现了环
                        fa[getf(K)]=getf(j);
                    }
                if(cir)continue;//连边不合法
                for(int j=1;j<=K;++j)//最前面的点必须和后面的一个点联通,否则就无法联通了
                    if(getf(0)==getf(j)){cir=true;break;}
                if(!cir)continue;
                int now=0,used=0;
                for(int j=0;j<K;++j)//当前存在合法的联通方案,因此当前的状态可以转移到另外的一个状态上去
                    if(!(now&(7<<(j*3))))//当前点不在任意一个联通块中
                    {
                        now|=++used<<(j*3);//新的联通块
                        for(int k=j+1;k<K;++k)//把所有在一个联通块里的点丢到状态里去
                            if(getf(j+1)==getf(k+1))
                                now|=used<<(k*3);
                    }
                g[i][p[now]]++;
            }
        }
        for(int i=1;i<=cnt;++i)
            for(int j=1;j<=cnt;++j)
                G.s[i][j]=g[i][j];
        G=fpow(G,n-K);
        int ans=0;
        for(int i=1;i<=cnt;++i)
            ans=(ans+1ll*G.s[i][1]*f[i])%MOD;
        printf("%d
    ",ans);
        return 0;
    }
    

    如果有问题的话大佬们可以指出

  • 相关阅读:
    Qt技巧、常用第三方库包含(qmake的.pro文件、CMakeLists.txt文件)
    STL资源
    debian 9 安装node angular
    debian 9 安装jenkins
    Consul 入门操作
    Docker 部署 postgresql 与 pgadmin4
    Docker File 与 Docker Compose
    Centos jdk
    Angular7 路由
    Centos Supervisor
  • 原文地址:https://www.cnblogs.com/zi-nai-boboyang/p/11437178.html
Copyright © 2011-2022 走看看