[NOI2007]生成树计数 (基于连通性的状态压缩动态规划+矩阵快速幂优化)

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/87883295

题意:n个点排成一排,每个点往它前k个点连边,求生成树个数。
n < = 1 e 15 , k < = 5 n<=1e15,k<=5
其实这个题很OI的。
考虑从左到右把点加入然后维护生成树个数,可以发现需要区分的状态只有前k的点的联通情况(快速判断成环或不连通),然后最小表示法基于连通性的状态压缩dp一发,然后就是喜闻乐见的发现状态数很少但转移次数很多,可以使用矩阵乘法其实我觉得怎么不求特征多项式然后来一发常系数线性递推呢?
原来没有插头就不用特别考虑0的情况啊。。。。。。
原来n<=k的时候可以用n(n-2)啊。。。。。。
原来按照本能把i->j的转移方案数放在a[i][j]的时候矩阵的柿子是右乘转移啊。。。。。。
原来对于n个点的联通情况用最小表示法只有 B e l l ( n ) Bell(n) 种啊(Bell(5)=52,Bell(6)=203,Bell(7)=877,Bell(8)=4140,Bell(9)=21147,Bell(10)=115975)

#include<bits/stdc++.h>
#define LL long long
#define mod 65521 
using namespace std;

LL n;
int k,id[100000],pos[300],cnt,mask[15],lt[15];

struct mat
{
	int a[210][210];
	mat(int d=0){memset(a,0,sizeof a);for(int i=1;i<=cnt;i++) a[i][i]=d;};
	mat operator *(const mat &B)const
	{
		mat ret=mat(0);
		for(int i=1;i<=cnt;i++)
			for(int j=1;j<=cnt;j++) if(a[i][j])
				for(int k=1;k<=cnt;k++) if(B.a[j][k])
					ret.a[i][k] = (ret.a[i][k] + a[i][j] * 1ll * B.a[j][k]) % mod;
		return ret;
	}
}A,B;

mat Pow(mat base,LL k)
{
	mat ret=mat(1);
	for(;k;k>>=1,base=base*base)
		if(k&1)
			ret=ret*base;
	return ret;
}

int encode()
{  
	int ret=0;
	static int id[15]={},cnt;
	memset(id,-1,sizeof id),cnt=id[0]=0;
	for(int i=0;i<k;i++) ret=ret*6+(id[mask[i]]==-1?id[mask[i]]=++cnt:id[mask[i]]);
	return ret;
}
void decode(int ret){ for(int i=k-1;i>=0;i--) mask[i]=ret%6,ret/=6; }

void dfs(int now,int val,int mx)
{
	if(now == k)
	{  
		pos[id[val]=++cnt] = val;
		return;
	}
	for(int i=1;i<=mx+1;i++) dfs(now+1,val*6+i,max(mx,i));
}

void dfs2(int now,int val,int mx)
{
	if(now == k)
	{  
		decode(val);
		 
		if(!mask[0] && !lt[0]) return;
		if(!lt[0])
		{
			bool flag = 0;
			for(int i=1;i<k;i++)
				if(mask[i] == mask[0])
					flag = 1;
			if(!flag) return;
		}
		
		int usd[10]={};
		for(int i=0;i<k;i++)
			if(lt[i])
			{
				usd[mask[i]] ++;
				if(usd[mask[i]] > 1) return;
			}
		int col = 9;
		for(int i=0;i<k;i++)
			if(usd[mask[i]])
				mask[i] = col;
		for(int i=0;i<k;i++)
			mask[i]=i<k-1?mask[i+1]:col;
		A.a[id[val]][id[encode()]]++;
		return;
	}
	for(int i=1;i<=mx+1;i++) 
	{
		lt[now]=1,dfs2(now+1,val*6+i,max(mx,i));
		lt[now]=0,dfs2(now+1,val*6+i,max(mx,i));
	}
}

void dfs3(int now,int val)
{
	if(now == min(n,1ll*k))
	{  
		B.a[1][id[val]] ++ ;
		return;
	}
	for(int j=0;j<(1<<now);j++)
	{
		decode(val);
		int usd[15]={};
		bool flag = 0;
		for(int p=0;p<now;p++)
			if(j>>p&1)
			{
				usd[mask[p]]++;
				if(mask[p] && usd[mask[p]]>1) flag=1;
			}
		int col = 9;		
		for(int p=0;p<now;p++)
			if((usd[mask[p]] && mask[p]) || (j>>p&1)) 
				mask[p]=col;
		mask[now] = col;
				
		if(!flag)
		{
			dfs3(now+1,encode());
		}
	}
}

int main()
{
	scanf("%d%lld",&k,&n);
	dfs(0,0,0);
	dfs2(0,0,0);
	dfs3(0,0);
	
	if(n >= k)
		B = B * Pow(A,n-k);
	int ans = 0;
	for(int p=1;p<=1;p++)
	{
		for(int i=0;i<min(n,1ll*k);i++) mask[i] = p;
		ans = (ans + B.a[1][id[encode()]])%mod;
	}
	printf("%d\n",(ans+mod)%mod);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/87883295