• [BZOJ1494][NOI2007]生成树计数 状压dp 并查集


    1494: [NOI2007]生成树计数

    Time Limit: 5 Sec  Memory Limit: 64 MB
    Submit: 793  Solved: 451
    [Submit][Status][Discuss]

    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

    HINT

     

    Source

     
    观察到k很小,n很大。
    考虑状压dp,矩阵快速幂优化。
    但是我们需要对状态进行离散化,还需有一些小技巧,可以看程序。
      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdlib>
      4 #include<cstdio>
      5 #include<algorithm>
      6 #include<cmath>
      7 #define maxn 150
      8 #define mod 65521
      9 using namespace std;
     10 int size[10]={1,1,1,3,16,125};
     11 long long n,k;int cnt;
     12 struct data {
     13     long long mat[maxn+1][maxn+1];
     14     data() {memset(mat,0,sizeof(mat));}
     15     data operator *(const data t1) {
     16         data tp;
     17         for(int i=0;i<=cnt;i++)
     18             for(int j=0;j<=cnt;j++)
     19                 for(int k=0;k<=cnt;k++) tp.mat[i][j]+=mat[i][k]*t1.mat[k][j],tp.mat[i][j]%=mod;
     20         return tp;
     21     }
     22 }A,B;
     23 int hash[20000],sta[20000];
     24 int fa[20000];
     25 int find(int x){return fa[x]==x?fa[x]:fa[x]=find(fa[x]);}
     26 void prepare(int pos,int now,int ma) {
     27     if(pos==k+1) {
     28         hash[now]=cnt++;
     29         sta[cnt-1]=now;
     30         return;
     31     }
     32     for(int i=1;i<=ma;i++) prepare(pos+1,now+(i<<(3*(pos-1))),ma+(i==ma));
     33 }
     34 int get() {
     35     int h[200];
     36     memset(h,-1,sizeof(h));
     37     int re=0;
     38     int cc=0;
     39     for(int i=2;i<=k+1;i++) {
     40         if(h[find(i)]==-1) h[find(i)]=++cc;
     41     }
     42     for(int i=2;i<=k+1;i++) {
     43         int now=h[find(i)];
     44         re+=(now<<(3*(i-2)));
     45     }
     46     return hash[re];
     47 }
     48 void build(int x,int add) {
     49     int now=sta[x];
     50     for(int i=1;i<=k+1;i++) fa[i]=i;
     51     for(int i=1;i<=k;i++) {
     52         for(int j=i+1;j<=k;j++) {
     53             if(((now>>((i-1)*3))&7)==((now>>((j-1)*3))&7)) {
     54                 int f1=find(i),f2=find(j);
     55                 if(f1!=f2) fa[f1]=f2;
     56             }
     57         }
     58     }
     59     for(int i=1;i<=k;i++) {
     60         if(add&(1<<(i-1))) {
     61             int f1=find(i),f2=find(k+1);
     62             if(f1==f2) return;
     63             fa[f1]=f2;
     64         }
     65     }
     66     bool flag=0;
     67     for(int i=2;i<=k+1;i++) {
     68         if(find(1)==find(i)) {flag=1;break;}
     69     }
     70     if(!flag) return;
     71     A.mat[get()][x]++;
     72 }
     73 data pow(data x,long long p) {
     74     data ans;
     75     for(int i=0;i<=maxn;i++) ans.mat[i][i]=1;
     76     while(p) {
     77         if(p&1) ans=ans*x;
     78         x=x*x;
     79         p>>=1;
     80          
     81     }
     82     return ans;
     83 }
     84 int main() {
     85     for(int i=0;i<=maxn;i++) B.mat[i][0]=1;
     86     scanf("%lld%lld",&k,&n);
     87     prepare(1,0,1);
     88     for(int i=0;i<cnt;i++)
     89         for(int j=0;j<(1<<k);j++) build(i,j);
     90     /*for(int i=0;i<cnt;i++) {
     91         for(int j=0;j<=k;j++) cout<<A.mat[i][j]<<' ';
     92         cout<<endl;
     93     }*/
     94     for(int i=0;i<cnt;i++) {
     95         int now=sta[i];
     96         int tmp[10]={};
     97         for(int j=1;j<=k;j++) tmp[now>>((j-1)*3)&7]++;
     98         for(int j=1;j<=k;j++) B.mat[i][0]*=size[tmp[j]];
     99     }
    100      
    101     A=pow(A,n-k);
    102     A=A*B;
    103     printf("%lld",A.mat[0][0]%mod);
    104 }
    View Code
    O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~
  • 相关阅读:
    Hibernate学习笔记
    Servlet:从入门到实战学习(3)---Servlet实例【图文】
    Servlet:从入门到实战学习(2)---Servlet生命周期
    Servlet:从入门到实战学习(1)---全·环境配置
    java复习(9)---数据库JDBC
    java复习(8)---I/O
    java复习(7)---集合类、泛型
    java复习(6)---异常处理
    C#尝试读取或写入受保护的内存。这通常指示其他内存已损坏
    C# TTS 文本转语音中断实现方式
  • 原文地址:https://www.cnblogs.com/wls001/p/7992272.html
Copyright © 2020-2023  润新知