UOJ Logo zhouzixuan的博客

博客

bzoj 1494: [NOI2007]生成树计数

2016-03-01 22:11:20 By zhouzixuan
【题目描述】:
    最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现:
    ·n个结点的环的生成树个数为n。
    ·n个结点的完全图的生成树个数为n^(n-2)。
    这两个发现让小栋欣喜若狂,由此更加坚定了他继续计算生成树个数的想法,他要计算出各种各样图的生成树数目。
    一天,小栋和同学聚会,大家围坐在一张大圆桌周围。小栋看了看,马上想到了生成树问题。如果把每个同学看成一个结点,邻座(结点间距离为1)的同学间连一条边,就变成了一个环。可是,小栋对环的计数已经十分娴熟且不再感兴趣。于是,小栋又把图变了一下:不仅把邻座的同学之间连一条边,还把相隔一个座位(结点间距离为2)的同学之间也连一条边,将结点间有边直接相连的这两种情况统称为有边相连,如图1所示。
    小栋发现利用通用方法,因计算过于复杂而很难算出来,而且用其他方法也难以找到更简便的公式进行计算。于是,他将图做了简化,从一个地方将圆桌断开,这样所有的同学形成了一条链,连接距离为1和距离为2的点。
    这样生成树的总数就减少了很多。小栋不停的思考,一直到聚会结束,终于找到了一种快捷的方法计算出这个图的生成树个数。可是,如果把距离为3的点也连起来,小栋就不知道如何快捷计算了。现在,请你帮助小栋计算这类图的生成树的数目。
    输入包含两个整数k, n,由一个空格分隔。k 表示要将所有距离不超 过k(含k)的结点连接起来,n 表示有n 个结点。
【题目解法】:
    注意到K<=5,因此我们不妨记录当前5个的点的连通性
    如何表示呢?我们可以考虑使用最小表示法
    很显然,我们可以将这5个点中同一个连通块的的点标成一样的编号
    每个连通块的编号和最左边的的同一连通块的编号一样
    然后从左到右每个连通块的第一个结点编号递增
    这样我们用一个6进制压缩即可(其中0表示没有结点)
    然后我数了数一共有76个合法的状态
    因此我们就可以暴力转移,然后矩阵乘法优化了
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=105;
const int M=10;
const int maxmod=65521;
long long n; int m,tot,a[N],trans[N][N],ans[N][N],c[N][N];
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
void init()
{
    m=getint(); n=getint(); tot=0;
}
namespace MinExpress
{
    int bit[M],first[M];
    bool judge(int x)
    {
        for (int i=m; i>=1; i--) bit[i]=x%6,x/=6; bool zero=true;
        for (int i=1; i<=m; i++) {if ((bit[i]==0)&&(!zero)) return false; if (bit[i]!=0) zero=false;}
        for (int i=0; i<=m; i++) first[i]=0; int maxid=0;
        for (int i=m; i>=0; i--) first[bit[i]]=i,maxid=max(maxid,bit[i]);
        for (int i=1; i<=maxid; i++) if (first[i]==0) return false;
        for (int i=2; i<=maxid; i++) if (first[i-1]>first[i]) return false;
        return true;
    }
    void solve()
    {
        int S=1; for (int i=1; i<=m; i++) S=S*6;
        for (int i=0; i<S; i++) if (judge(i)) a[++tot]=i;
        //printf("%d\n",tot);
    }
}
namespace Matrix
{
    int bit[M],opt[M],id[M],color[M]; bool v[M];
    void Zip(int x)    
    {
        for (int i=m; i>=1; i--) bit[i]=x%6,x/=6;
        for (int i=1; i<=m; i++) printf("%d",bit[i]);
    }
    void solve()
    {
        for (int i=1; i<=tot; i++)
        {
            int S=(1<<m)-1;
            for (int j=0; j<=S; j++)
            {
                for (int k=m,x=a[i]; k>=1; k--,x/=6) bit[k]=x%6;
                for (int k=m,x=j; k>=1; k--,x/=2) opt[k]=x%2; bool flag=true;
                for (int k=1; k<=m; k++) if ((bit[k]==0)&&(opt[k]!=0)) {flag=false; break;}
                for (int k=0; k<=6; k++) v[k]=false;
                for (int k=1; k<=m; k++)
                {
                    if (opt[k]==0) continue;
                    if (v[bit[k]]) {flag=false; break;}
                    v[bit[k]]=true; bit[k]=m+1;
                }
                if (!flag) continue;
                for (int k=1; k<=m; k++) if (v[bit[k]]) bit[k]=m+1;
                int newS=0,idx=0; bit[m+1]=m+1;
                for (int k=0; k<=6; k++) color[k]=0;
                for (int k=1; k<=m+1; k++) color[bit[k]]++;
                if ((color[bit[1]]==1)&&(bit[1]!=0)) continue;
                for (int k=0; k<=6; k++) id[k]=0;
                for (int k=2; k<=m+1; k++)
                {
                    if (bit[k]==0) continue;
                    if (id[bit[k]]==0) id[bit[k]]=++idx;
                    newS=newS*6+id[bit[k]];
                }
                int ii=0;
                for (int k=1; k<=tot; k++) if (newS==a[k]) {ii=k; break;}
                if (ii==0) printf("fuck\n");
                trans[i][ii]++; //Zip(a[i]); printf(" "); Zip(a[ii]); printf("\n");
            }
        }
    }
}
void mul1()
{
    for (int i=1; i<=tot; i++)
        for (int j=1; j<=tot; j++)
        {
            c[i][j]=0;
            for (int k=1; k<=tot; k++) c[i][j]=(c[i][j]+(long long)ans[i][k]*trans[k][j]%maxmod)%maxmod;
            c[i][j]%=maxmod;
        }
    for (int i=1; i<=tot; i++)
        for (int j=1; j<=tot; j++)
        ans[i][j]=c[i][j];
}
void mul2()
{
    for (int i=1; i<=tot; i++)
        for (int j=1; j<=tot; j++)
        {
            c[i][j]=0;
            for (int k=1; k<=tot; k++) c[i][j]=(c[i][j]+(long long)trans[i][k]*trans[k][j]%maxmod)%maxmod;
            c[i][j]%=maxmod;
        }
    for (int i=1; i<=tot; i++)
        for (int j=1; j<=tot; j++)
        trans[i][j]=c[i][j];
}
void Debug()
{
    for (int i=1; i<=tot; i++)
    {
        for (int j=1; j<=tot; j++)
        printf("%d",trans[i][j]);
        printf("\n");
    }
}
void solve()
{
    for (int i=1; i<=tot; i++) ans[i][i]=1;
    while (n)
    {
        if (n%2==1) mul1();
        mul2(); n/=2;
    }
    int S=0,id=0;
    for (int i=1; i<=m; i++) S=S*6+1;
    for (int i=1; i<=tot; i++) if (a[i]==S) {id=i; break;}
    if (id==0) printf("fuck\n");
    printf("%d\n",ans[1][id]);
}
int main()
{
    init();
    MinExpress::solve();
    Matrix::solve();
    solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。