题意

F.A.Qs Home Discuss ProblemSet Status Ranklist Contest 入门OJ ModifyUser  autoint Logout 捐赠本站

Problem 1494. -- [NOI2007]生成树计数

1494: [NOI2007]生成树计数

Time Limit: 5 Sec  Memory Limit: 64 MB
Submit: 1024  Solved: 592
[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

[Submit][Status][Discuss]

HOME
Back


한국어 
中文 
فارسی 
English 
ไทย

版权所有 ©2008-2018 大视野在线测评 | 湘ICP备13009380号
Based on opensource project hustoj.

分析

参照xyz32768的题解。

定义状态: \(f[i][S]\)表示\(i\)个点的图,编号差\(≤k\)的点对之间有连边,现选出一些边,连通第\(1\)个点到第\(i−k\)个点,第\(i−k+1\)个点到第\(i\)个点的连通性为\(S\),第\(i−k+1\)个点到第\(i\)个点至少存在一点与前\(i−1\)个点连通,且选出的边无环的方案数。

注意这里的\(S\)压缩连通性使用的是最小表示法: 对连通块进行标号,从\(1\)到连通块个数,首次出现位置越靠前的连通块编号越小。

会发现有效\(S\)的个数是贝尔数,即第二类斯特林数的前缀和。 \(k=5\)时状态数只有\(52\) 。

如何求\(f[i+1][T]\)从\(f[i][S]\)的转移系数呢?
我们可以对于一个\(S\),\(2^k\)枚举第\(i−k+1\)个点到第\(i\)个点是否与\(i+1\)连边,大力计算即可。
但要注意:

  1. 如果第\(i−k+1\)个点在第\(i−k+1\)到第\(i\)个点中单独属于一个连通块,那么\(i−k+1\)和\(i+1\)间必须连边,否则不能保证「连通前\(i−k+1\)个点」。
  2. 不能由\(i\)连向第\(i−k+1\)个点到第\(i\)个点中的同一个连通块,否则会形成环。

发现转移系数不变且为一阶,因此直接上矩阵快速幂。

代码

参照Icefox_zhx的代码。dfs写有许多好处。

我们可以先用一个DFS预处理出所有可能出现的连通性的状态。然后再枚举连通性状态S以及下一个点和S里的K个点中的哪些点连边,再判断从连通性状态S转移出来的新状态S′是否是合法的,若合法,在邻接矩阵里,标记从S到S′的方案数加1.

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
    while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int M=7,N=60,mod=65521;
int m;ll n;
int temp[M],sta[N][M],tot,ini[N];
void init_sta(int id){  //预处理所有状态的初始种类数
    static int cnt[M];
    memset(cnt,0,sizeof cnt);
    for(int i=1;i<=m;++i) ++cnt[sta[id][i]];
    ini[id]=1;
    for(int i=1;cnt[i];++i)if(cnt[i]>2) ini[id]*=pow(cnt[i],cnt[i]-2);
    //n个点的完全图的生成树个数为n^(n-2)个
}
void dfs_sta(int i,int x){ //前i-1个点分成了x个连通块
    if(i==m+1) return memcpy(sta[++tot],temp,sizeof temp),init_sta(tot);
    for(int j=1;j<=x+1;++j) temp[i]=j,dfs_sta(i+1,max(j,x));
}

struct matrix{
    int a[N][N];
    il int*operator[](int x){return a[x];}
    il co int*operator[](int x)co{return a[x];}
    matrix operator*(co matrix&b)co{
        matrix c;
        memset(c.a,0,sizeof c.a);
        for(int i=1;i<=tot;++i)
            for(int j=1;j<=tot;++j)
                for(int k=1;k<=tot;++k)
                    c[i][j]=(c[i][j]+(ll)a[i][k]*b[k][j])%mod;
        return c;
    }
    matrix operator^(ll k){
        matrix x=*this,re;
        memset(re.a,0,sizeof re.a);
        for(int i=1;i<=tot;++i) re[i][i]=1;
        for(;k;k>>=1,x=x*x)
            if(k&1) re=re*x;
        return re;
    }
}trans;
bool f[M],edge[M]; //f[i]:i连通块是否已连过,edge[i]:i位置是否连边了
bool issame(int a[],int b[]){
    for(int i=1;i<=m;++i)
        if(a[i]!=b[i]) return 0;
    return 1;
}
void get_tra(int id){ //求sta[id]此时转移到的状态a
    static int a[M];
    memcpy(a,sta[id],sizeof a);
    for(int i=1;i<=m;++i)if(edge[i]){
        if(a[m+1]==0) a[m+1]=a[i];
        else for(int x=a[i],j=1;j<=m;++j)if(a[j]==x) a[j]=a[m+1];
    }
    for(int i=1;i<=m;++i) a[i]=a[i+1]; //把状态往前挪一个
    static int num[M],cnt;
    memset(num,0,sizeof num),cnt=0;
    for(int i=1;i<=m;++i){ //重新标号,使得满足最小表示法
        if(!num[a[i]]) num[a[i]]=++cnt;
        a[i]=num[a[i]];
    }
    for(int i=1;i<=tot;++i)if(issame(a,sta[i]))
        return ++trans[id][i],void();
}
void dfs_tra(int id,int i){
    if(i==m+1) return get_tra(id);
    dfs_tra(id,i+1);
    if(!f[sta[id][i]]){
        f[sta[id][i]]=1,edge[i]=1;
        dfs_tra(id,i+1);
        f[sta[id][i]]=0,edge[i]=0;
    }
}
int main(){
//  freopen(".in","r",stdin),freopen(".out","w",stdout);
    read(m),read(n);
    dfs_sta(1,0); //预处理出k个点的所有连接状态,用最小表示法表示,以及每种状态的初始种类数
    for(int i=1;i<=tot;++i){
        memset(f,0,sizeof f);
        memset(edge,0,sizeof edge);
        bool flag=1;
        for(int j=2;j<=m;++j)
            if(sta[i][j]==1) {flag=0;break;}
        if(flag) f[1]=1,edge[1]=1,dfs_tra(i,2); //1必须连这个了。
        else dfs_tra(i,1);
    }
    trans=trans^(n-m);
    int ans=0;
    for(int i=1;i<=tot;++i) ans=(ans+(ll)trans[i][1]*ini[i])%mod;
    printf("%d\n",ans);
    return 0;
}

BZOJ1494 [NOI2007]生成树计数的更多相关文章

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

    1494: [NOI2007]生成树计数 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 793  Solved: 451[Submit][Status][ ...

  2. [NOI2007]生成树计数环形版

    NOI2007这道题人类进化更完全之后出现了新的做法 毕姥爷题解: 于是毕姥爷出了一道环形版的这题(test0814),让我们写这个做法 环形的情况下,k=5的时候是162阶递推. 求这个递推可以用B ...

  3. NOI2007 生成树计数

    题目 首先我要吐槽,这题目就是坑,给那么多无用的信息,我还以为要根据提示才能做出来呢! 算法1 暴力,傻傻地跟着提示,纯暴力\(40\)分,高斯消元\(60\)分. 算法2 DP!一个显然的东西是,这 ...

  4. [BZOJ1494]生成树计数

    [BZOJ1494] [NOI2007]生成树计数 Description 最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现:·n个结点的环的生成树个数为n.·n个结点的完全图的生成树 ...

  5. 【BZOJ1494】【NOI2007】生成树计数(动态规划,矩阵快速幂)

    [BZOJ1494][NOI2007]生成树计数(动态规划,矩阵快速幂) 题面 Description 最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现: ·n个结点的环的生成树个数为 ...

  6. 【BZOJ1002】【FJOI2007】轮状病毒(生成树计数)

    1002: [FJOI2007]轮状病毒 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1766  Solved: 946[Submit][Status ...

  7. SPOJ 104 HIGH - Highways 生成树计数

    题目链接:https://vjudge.net/problem/SPOJ-HIGH 解法: 生成树计数 1.构造 基尔霍夫矩阵(又叫拉普拉斯矩阵) n阶矩阵 若u.v之间有边相连 C[u][v]=C[ ...

  8. Luogu P5296 [北京省选集训2019]生成树计数

    Luogu P5296 [北京省选集训2019]生成树计数 题目链接 题目大意:给定每条边的边权.一颗生成树的权值为边权和的\(k\)次方.求出所有生成树的权值和. 我们列出答案的式子: 设\(E\) ...

  9. Loj 2320.「清华集训 2017」生成树计数

    Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...

随机推荐

  1. Java:多个文档合并输出到一个文档

    多个文档合并输出到一个文档 方法:Java NIO package First; import java.io.File; import java.io.FileInputStream; import ...

  2. 【转载】JVM系列三:JVM参数设置、分析

    不管是YGC还是Full GC,GC过程中都会对导致程序运行中中断,正确的选择不同的GC策略,调整JVM.GC的参数,可以极大的减少由于GC工作,而导致的程序运行中断方面的问题,进而适当的提高Java ...

  3. sass 继承 占位符 %placeholder

    @extend //SCSS .btn { border: 1px solid #ccc; padding: 6px 10px; font-size: 14px; } .btn-primary { b ...

  4. C语言获取系统时间的几种方式

    C语言获取系统时间的几种方式 2009-07-22 11:18:50|  分类: 编程学习 |字号 订阅     C语言中如何获取时间?精度如何? 1 使用time_t time( time_t * ...

  5. 201621123001《Java程序设计》第5周学习总结

    1.本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 Answer: 本周重要关键词有接口,Comparator接口,Comparable接口,interface关键字,抽象类等. 1 ...

  6. leetcode57:插入区间

    给出一个无重叠的 ,按照区间起始端点排序的区间列表. 在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间). 示例 1: 输入: intervals = [ ...

  7. Nodejs安装express提示不是内部或外部命令

    安装nodejs安装包后,通过npm安装express后,运行express提示" express"不是内部或外部命令. c:\express -t ejs web 原因是版本问题 ...

  8. Day8作业及默写

    1,有如下文件,a1.txt,里面的内容为: 老男孩是最好的培训机构, 全心全意为学生服务, 只为学生未来,不为牟利. 我说的都是真的.哈哈 分别完成以下的功能: 将原文件全部读出来并打印. with ...

  9. spark:ML和MLlib的区别

    ML和MLlib的区别如下: ML是升级版的MLlib,最新的Spark版本优先支持ML. ML支持DataFrame数据结构和Pipelines,而MLlib仅支持RDD数据结构. ML明确区分了分 ...

  10. ibatis集成封装之路(to mysql)

    hello <tx:annotation-driven transaction-manager=" "/> 插入记录ID的坑 https://renjieguixion ...