BZOJ 1494 [NOI2007]生成树计数

BZOJ1494】【NOI2007生成树计数 【题目链接】 点击打开链接 【思路要点】 写个矩阵树定理的暴力,求出对于输入的kkk,NNN在100以内的结果。 运行BM算法,我们发现答案是一个至多46阶的线性递推。 然后求它的第NNN项就好了。 时间复杂度O(R4+R2LogN)O(R4+R2LogN)O(R^4+R^2LogN),其中R=46R=46R=46。 【代码】 ... 阅读详情

BZOJ 1494 [NOI2007]生成树计数

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1494

题目大意: 给定 n 个点的无向图 。节点编号1......n
i 个点与第j个点有边.当且仅当:ijk
计算 n 个点时图的生成树数量。2k5  , 2n1015
答案 mod 65521

所以我们基于连通性进行递推。并通过构造矩阵进行快速幂。

为什么要基于连通性。如何表示状态。
首先:
当我们考虑加入一个新的点时。如果它不是我们加入的最后一个点。那么我们没有必要一定要使得加入这个点后 。建立一些边后。这个森林会成为一个树。
但我们需要使得更远点。与最后 k 个点有链接。

如果更远的点与最后k个点没有链接。新加入的点又不可能与之建立联系。

导致更远的点断开了链接。就不可能生成一个树。

其次。连通性其实就是特征。根据不同的连通性决定的一类森林进行转移。相同连通性的森林转移到另一连通性时。方案是一样的。

所以可以基于连通性进行递推。

那么如何表示后k点的连通性呢。

我们用 k 进制数字来表示。[0,k1]
k 个节点的第t个节点所在集合编号就是这个数字的第 t 位数码。

可能会出现重复状态:

例如:
0 1 11 2 2

这两个状态是一样的。
那么我们调整对状态表示的定义。对于确定的连通性。
某一点所在集合的编号如何确定是我们关心的。
那么不如所有集合中最早出现的点的名次作为集合编号。从 0 开始编号。

例如:
2 1 2 3 0

他的状态就是:
0 1 0 2 3

我们计算出所有可能的状态。并按顺序对这些状态编号。从0开始。

那么剩下的是构造转移矩阵:
对于状态 a b转移的方案数量我们记为: M[b][a]
显然之前在一个联通块的点不可能分开。
所以如果在 a 中是连通的在b中变为不连通是不可能的.这时候转移方案是0。
其次,如果在 a 中是不连通的。在b中连通的。这时候。如何两个点与最新的点不在一个集合。那也是不可能的。之所以连通只可能是新的点建立 了链接。所以此时方案也是0
除了这两个情况之外。我们开始计算转移方案:
对于与新的点在一个联通块的点。之前状态所在的集合在 k 个点中占有的点数就是新点与之链接的方案数。根据乘法原理:
M[b][a]=c1c2..cr
这表示让不相关的 r 个集合连通的方案只需要分开计算,相乘即可。

其次。构造出来的矩阵无法从0开始递推。

因为在n<k时情况的特殊性。

我们需要计算。 k 个点时。在不同连通状态下的方案数。作为右侧向量。

k递推到 n

对于这一步。与之前的想法差不多。对于第i个连通块的方案。

第一步:构造关于第i个连通块的子图。
第二步:构造关于这个子图的基尔霍夫矩阵。
第三步:对这个矩阵的 n1 阶进行对角化或变为上下三角。
第四步:对角化后计算对角线的乘机即位方案数。(即行列式的值)
单独计算每个连通块的搭建方案后乘即可。
这样就可以愉快的矩阵快速幂了。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cmath>
#define MAXN 1000
using namespace std;
typedef long long LL;
const int mod=65521;

int T[100][6],dep;
int D[100];
bool vis[6];
int C[6];
int A[5];
int M[60][60];
int E[10][10];
int tmE[10][10];
int tma[6],tmb[6];

int cmp(int k,int a,int b)  //a-->b
{
    for(int i=0;i<k;i++)
    {
        tma[i]=T[a][i];
        tmb[i]=T[b][i];
    }
    for(int i=0;i<k-1;i++)
        for(int j=i+1;j<k-1;j++)
        {
            if(tma[i+1]==tma[j+1]&&tmb[i]!=tmb[j])return 0;
            if(tma[i+1]!=tma[j+1]&&tmb[i]==tmb[j]&&tmb[i]!=tmb[k-1])return 0;
        }
    memset(vis,0,sizeof vis);
    int ans=1;
    for(int i=0;i<k-1;i++)
    {
        if(tmb[i]!=tmb[k-1])continue;
        if(vis[tma[i+1]])   continue;
        vis[tma[i+1]]=true;
        int c=0;
        for(int j=0;j<k;j++)
            if(tma[j]==tma[i+1])c++;
        ans=(LL)ans*c%mod;
    }
    return ans;
}

struct mat
{
    int m[60][60];
    mat()
    {
        memset(m,0,sizeof m);
    }
    void e()
    {
        for(int i=0;i<60;i++)m[i][i]=1;
    }
    mat operator *(const mat & a)const
    {
        mat b;
        for(int i=0;i<dep;i++)
            for(int j=0;j<dep;j++)
                for(int k=0;k<dep;k++)
                    b.m[i][j]=((LL)b.m[i][j]+(LL)m[i][k]*a.m[k][j])%mod;
        return b;
    }
};

int Iv[mod+10];

int Kirchhof(int n)
{
    n--;
    int r=0;
    for(int i=0; i<n ;i++)
    {
        for(int j=r; j<n ;j++)
            if(tmE[j][i]>0)
            {
                for(int k=i;k<n;k++)
                    swap(tmE[j][k],tmE[r][k]);
                break;
            }
        if(tmE[r][i]==0)continue;
        for(int j=0;j<n;j++)
            if(j!=r&&tmE[j][i]>0)
            {
                int tm=(LL)tmE[j][i]*Iv[tmE[r][i]]%mod;
                for(int k=i;k<n;k++)
                    tmE[j][k]=(tmE[j][k]-(LL)tm*tmE[r][k]%mod+mod)%mod;
            }
        r++;
    }
    int ans=1;
    for(int i=0;i<n;i++)
        ans=(LL)ans*tmE[i][i]%mod;
    return ans;
}

int B[100];

int clat(int k,int d)
{
    int ans=1;
    memset(vis,0,sizeof vis);
    for(int i=0;i<k;i++)
    {
        if(vis[T[d][i]])    continue;
        vis[T[d][i]]=true;
        memset(tmE,0,sizeof tmE);
        int u=0;
        for(int j=0;j<k;j++)
            if(T[d][j]==T[d][i])C[u++]=j;
        for(int j=0;j<k;j++)
            for(int h=0;h<k;h++)
            {
                if(T[d][j]!=T[d][i])continue;
                if(T[d][h]!=T[d][i])continue;
                int a=(int)(lower_bound(C,C+u,j)-C);
                int b=(int)(lower_bound(C,C+u,h)-C);
                tmE[a][b]=E[j][h];
            }
        for(int j=0;j<u;j++)
            for(int h=0;h<u;h++)
                if(j!=h)
                    tmE[j][j]=(tmE[j][j]-tmE[j][h]+mod)%mod;
        ans=(LL)ans*Kirchhof(u)%mod;
    }
    return ans;
}

int slove(LL n,int k)
{
    if(n<=(LL)k)
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                tmE[i][j]=E[i][j];
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(i!=j)
                    tmE[i][i]=(tmE[i][i]-tmE[i][j]+mod)%mod;
        return Kirchhof((int)n);
    }
    mat a,tmp;
    tmp.e();
    for(int i=0;i<dep;i++)
        for(int j=0;j<dep;j++)
            a.m[i][j]=M[i][j];
    n-=k;
    while(n)
    {
        if(n&1)
            tmp=tmp*a;
        a=a*a;
        n>>=1;
    }
    int ans=0;
    for(int i=0;i<dep;i++)
        ans=(ans+(LL)tmp.m[0][i]*clat(k,i)%mod)%mod;
    return ans;
}

int main ()
{
    Iv[1]=1;
    for(int i=2;i<mod;i++)  Iv[i]=mod-(LL)(mod/i)*Iv[mod%i]%mod;
    LL n;
    int k;
    scanf("%d %lld",&k,&n);
    int size=0,ten=1,bi=0;

    while(bi<k)
    {
        ten*=10;
        size=size*10+(k-1);
        bi++;
    }

    ten/=10;
    size+=1;
    for(int i=0;i<size;i++)
    {
        for(int K=0,u=ten,d=i;K<k;K++)
        {
            A[K]=d/u;
            d=d%u;
            u/=10;
        }
        int cnt=0,flag=false;
        for(int K=0;K<k;K++)
        {
            if(A[K]>cnt)
            {
                flag=true;
                break;
            }
            if(A[K]==cnt)   cnt++;
        }
        if(flag)continue;
        for(int t=0;t<k;t++)    T[dep][t]=A[t];
        dep++;
    }
    for(int i=0;i<dep;i++)
        for(int j=0;j<dep;j++)
            M[i][j]=cmp(k,j,i);
    for(int i=0;i<10;i++)
        for(int j=0;j<10;j++)
        {
            if(i==j)continue;
            if(abs(j-i)<=k)E[i][j]=mod-1;
        }
    printf("%d\n",slove(n,k));
    return 0;
}
如何利用三阈值游程理论识别干旱事件_python 如果当前时间点与上一个干旱事件的结束位置相隔不超过一个月(`index - current_event['end_index'] 阅读详情

相关推荐

回归预测 | MATLAB实现SVR(支持向量机回归)K折交叉验证

回归预测 | MATLAB实现SVR(支持向量机回归)K折交叉验证 目录回归预测 | MATLAB实现SVR(支持向量机回归)K折交叉验证基本介绍划分原理留一划分交叉验证K折交叉数据下载程序设计参考资料致谢 基本介绍 在机器学习里,通常来说我们不能将全部用于数据训练模型,否则我们将没有数据集对该模型进行验证,从而评估我们的模型的预测效果。为了解决这一问题,有如下常用的方法: 第一种是最简单的,也是很容易就想到的。我们可以把整个数据集分成两部分,一部分用于训练,一部分用于验证,这也就是我们经常提到的训练集

机器学习之心的博客,关注并私信文章链接,获取对应文章源码和数据。 6314

数论初步

一、唯一分解定理基本描述任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积.理解换句话说,它来指导我们分解质因数。 想想在分解质因数的时候,我们使用的是短除法,先试除2,然后试除3,其实就是用到的唯一分解定理。代码找出num的所有质因数void findfactor(int num){ v.clear(); int i = 2; while(nu

Pengwill's Blog 698

精品PPT-GNSS/GPS/RTK卫星定位原理及应用课件全套(19份).zip

精品PPT,GNSS卫星定位原理及应用课件全套,共19份。01 GNSS 绪论.pptxx02 时空框架.pptxx03 时空框架系统.pptxx04 GPS卫星轨道与导航电文.pptx04-2 GPS卫星的信号结构.pptx05 GPS定位中的误差源,ppt06 GPS定位中的误差源.pptx07 GPS定位中的误差源.pptx08 基本观测量与GPS定位.pptx09 观测值的线性组合.pptx010周跳与整周模糊度.pptx011相对定位.pptx012 差分GPS.pptx013 GPS网设计及数据处理.pptxGPS卫星定位原理及应用.pptxx地信GNSS高技术讲座.pptxKlobuchar电离层时延改正模型.docMelbourne-Wubbena组合观测值.docNMF对流层改正模型.doc

图论 —— 生成树 —— 生成树计数

【概述】 给出一个由 n 个点和 m条边构成的简单无向加权图,有时需要对生成树计数或对最小生成树计数。 当对生成树计数时,利用基尔霍夫矩阵的Matrix-Tree 定理即可解决,而对最小生成树计数时,根据数据范围的不同,所采用的方法也不同。 关于基尔霍夫矩阵:点击这里 【生成树计数】 对于生成树计数,一般采用矩阵树定理(Matrix-Tree 定理)来解决。 Matrix-Tre...

Alex_McAvoy的博客 8736

图论(7)生成树及其计数

(一)、生成树的概念与性质 1.生成树的概念 定义1:图G的一个生成子图T如果是树,称它为G的一棵生成树。若T为森林,称它为G的一个生成森林。 首先回忆一下生成子图的概念,如果一个子图包含原图的所有顶点,则称该子图为原图的生成子图。 G中生成树的边称为树枝,G中非生成树的边称为弦。 2.生成树的性质 定理1:每个连通图至少包含一棵生成树。 利用破...

向上吧贺凯 1万+

图论(六)——生成树数量计算&&矩阵树定理求解生成树个数

1、生成树的概念和性质 \quad定义:图G的一个生成子图T如果是树,称它为G的一棵生成树;若T为森林,称它为G的一个生成森林。(生成子图:包含原图所有顶点,边不管) 生成树不唯一 生成树的边称为树枝,G中非生成树的边称为弦 1、每个连通图至少包含一棵生成树 \quad证明:如果连通图G是树,则其本身是一棵生成树;若连通图G中有圈C,则去掉C中一条边后得到的图仍然是连通的,这样不断去掉G中圈,...

qq_40438165的博客 1万+

[NOI2007]生成树计数

题意 一个nnn个点的树,点iii只能向[i−k,i−1][i−k,i−1][i-k,i-1]内的点连边,求有标号生成树的个数 n≤1015,k≤5n≤1015,k≤5n\le10^{15},k\le5 题解 题面误导向系列,显然不能像他说的那样做吧 因为发现kkk特别小,所以可以考虑用状压表示iii和前k−1k−1k-1个点的联通情况 考虑DP,f[i][j]DP,f[i][j]...

BeNoble_的博客 615

bzoj1494: [NOI2007]生成树计数 (状压+矩阵倍增)

bzoj1494: [NOI2007]生成树计数 题意 求使所有点连成一棵生成树的方案数 代码 #include &amp;lt;cstdio&amp;gt; #include &amp;lt;cstring&amp;gt; #include &amp;lt;cmath&amp;gt; #include &amp;lt;algorithm&amp;gt; using namespace std; #define M 65521

生活不易,多才多艺 384

BZOJ 1494: [NOI2007]生成树计数

这题确实比较难, 所以我写解题报告也尽量从浅显处着手,希望能够帮到大家。 题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1494 题目大意:有n(n 算法: 从这道题的数据范围,不难看出要使用状态压缩。 首先,我们来确定前k个点的状态。

frog的小池塘 2667

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

BZOJ1494】【NOI2007生成树计数(动态规划,矩阵快速幂) 题面 Description 最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现: ·n个结点的环的生成树个数为n。 ·n个结点的完全图的生成树个数为n^(n-2)。这两个发现让小栋欣喜若狂,由此更加坚定了他继续计算生成树个数的 想法,他要计算出各种各样图的生成树数目。一天,小栋和同学聚会,大家围坐在一张大圆桌...

weixin_30740581的博客 163

BZOJ 1494】【NOI 2007生成树计数

http://www.lydsy.com/JudgeOnline/problem.php?id=1494 这道题。。因为k很小,而且我们只关心连续的k个节点的连通性,所以把连续的k个点轮廓线上的连通性的最小表示当做状态来转移。 转移可以构造一个矩阵,构造矩阵不难想,但挺麻烦的。。。 时间复杂度\(O(Mlen2^kk^2+Mlen^3logn)\),Mlen最坏情况下为52。 #incl...

as2886089的博客 91

bzoj1494: [NOI2007]生成树计数

传送门 将k个点的连通性用最小表示法压成状态,那么最多有52种状态 最小表示法中,f[i]表示最小的与其联通的点编号。 计算出每个状态的生成树个数,作为初始行向量A 对于每种状态考虑新加入一个点并向这k个点连边,每种连法可以转移到哪些状态,得到转移矩阵B 那么答案就是A∗Bn[所有点都连通的状态] 就是代码炒鸡长。#include<cmath> #include<cstdio> #inc

zyy大蒟蒻的博客 707

BZOJ 1494 NOI2007 生成树计数 状压DP+矩阵乘法

题目大意:给定n(n≤1015)n(n\leq 10^{15})个点,编号差不超过k(k≤5)k(k\leq 5)的点之间有连边,问生成树个数 将kk个点的连通性用最小表示法压成状态,那么最多有5252种状态 计算出每个状态的生成树个数,作为初始行向量AA 对于每种状态考虑新加入一个点并向这kk个点连边,每种连法可以转移到哪些状态,得到转移矩阵BB 那么答案就是A∗Bn[A*B^n[所有点都

世界 2446

bzoj 1494: [NOI2007]生成树计数

55555555555555555555555555555555被虐哭了 调了两个小时才调出来,简直不要太坑。 论文还是比较良心的,就是初始矩阵好难构造的说。 最后迫不得已写了个很挫的方法构造。 #include #include #include #include using namespace std; typedef long long ll; const int p=65521;

月下沙茶树 712

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

1494: [NOI2007]生成树计数 Time Limit:5 SecMemory Limit:64 MBSubmit:793Solved:451[Submit][Status][Discuss] Description 最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现: ·n个结点的环的生成树个数为n。 ·n个结点的完全图的生成树个数为n...

weixin_30872867的博客 208

NOI2007/BZOJ1494生成树计数 插头DP

原题走这里 一看数据范围就知道是矩阵快速幂优化 首先,设当前点为iii, 接着就会发现,iii不可能与i−Ki−Ki-K之前的点相连, 因此当前点的连通性只与i−Ki−Ki-K到i−1i−1i-1共K个点的连通性有关。 于是我们可以用d(i,S)d(i,S)d(i,S)来表示当前点为iii,连通性为SSS的情况时的情况数。 使用最小表示法,能够把连通性实质上不同的情况优化到只剩五十几种...

CRTorlonia的博客 484

bzoj1494Noi2007生成树计数

题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1494 sol :前排膜拜http://blog.csdn.net/qpswwww/article/details/45362639    虽然dalao们说了一些最小表示法啊什么k=5时只有52种状态啊balabala,然而我并不会.......    因为k很小,可以考虑用状压...

weixin_30912051的博客 93

[BZOJ1494][NOI2007]生成树计数(状压dp+矩阵乘法)

Address https://www.lydsy.com/JudgeOnline/problem.php?id=1494 Solution 定义状态: f[i][S]f[i][S]f[i][S] 表示 iii 个点的图,编号差 ≤k≤k\le k 的点对之间有连边,现选出一些边,连通第 111 个点到第 i−ki−ki-k 个点,第 i−k+1i−k+1i-k+1 个点到第 iii ...

女装的你,如此好看! 416

[BZOJ 1494][NOI 2007]生成树计数(状压DP+最小表示法+矩阵乘法)

题目链接http://www.lydsy.com/JudgeOnline/problem.php?id=1494思路由于每个点ii只能和点i−Ki-K到i−1i-1连边,因此点i−K+1i-K+1和其前面的所有点一定是都在同一个联通块。因此我们只需要用一个长度为KK的序列来表示点i−K+1i-K+1到点ii的连通性,而其他的点一定是连通的,就不必表示连通性了。这个连通性的序列可以用最小表示法压缩成一

某沙茶的OI代码库 2303

BZOJ 1494 [NOI2007]生成树计数 矩阵乘法+DP

题解: 这种数据范围果断矩阵乘法,其实dp什么的也特别显然,就是求转移矩阵特别恶心。 用最小表示法(表示现学的压力比较大)表示连通性。 能多暴力就多暴力的枚举,反正数据范围小~ 唯一需要注意的是转移的时候有两种情况是不合法的: 1、第(i-k)个点不和第(i-k+1)到i个点连通 2、第i个点连到已经在一个连通块中的两个点 wa了半天,然后不想做了的时候突然发现ac了。。莫名其...

weixin_30776863的博客 124
上一篇: 2017 ICPC hihocoder 1636
下一篇: 51nod 1803 森林的直径
佐理慧
博客等级 码龄9年 164粉丝 79原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值