luogu P2144 [FJOI2007]轮状病毒

本文详细解析洛谷P2144生成树计数问题,提供两种解题思路:一是利用基尔霍夫矩阵与矩阵树定理,通过高斯消元法求解;二是通过观察规律,找到生成树数量的递推公式,实现高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:

h e h e . . . hehe... hehe...

题目传送门:

https://www.luogu.org/problemnew/show/P2144

题意:

有一个类似与下图的形状,题目给出 n n n,表示蓝色点的数目,求生成树的方案。
在这里插入图片描述

思路 1 1 1

细想一下,这不就是基尔霍夫矩阵&矩阵树定理吗。
考虑 n ≥ 2 n≥2 n2时的的 A , D A,D A,D矩阵。
发现中间的度为 n n n,其余点的度均为 3 3 3
在考虑每两个点之间的连边情况确定 A A A即可。
然后就可以采用高斯消元解决即可。

代码 1 1 1

没有打高精度(只有 40 p t s 40pts 40pts)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
	LL D[110][110],A[110][110],K[110][110];
	int n;
LL Matrix_tree()//在这题中,0表示中间的点,1~n表示周的点 
{
	if(n==1)
	{
		D[0][0]=D[1][1]=1;
	}
	else if(n==2)
	{
		D[0][0]=2,D[1][1]=D[2][2]=3;
		A[0][1]=A[1][0]=A[0][2]=A[2][0]=1,A[1][2]=A[2][1]=2;
	}
	else
	{
		D[0][0]=n;
		for(int i=1;i<=n;i++)
		{
			D[i][i]=3;
			A[0][i]=A[i][0]=1;
		}
		for(int i=2;i<=n;i++)
			A[i][i-1]=A[i-1][i]=1;
		A[1][n]=A[n][1]=1;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			K[i][j]=D[i][j]-A[i][j];
	LL ans=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
			while(K[j][i])
			{
				int t=K[i][i]/K[j][i];
				for(int k=i;k<=n;k++) K[i][k]-=K[j][k]*t;
				for(int k=i;k<=n;k++) swap(K[i][k],K[j][k]);
				ans=-1;
			}
		ans*=K[i][i];
	}
	return abs(ans);
}
int main()
{
	scanf("%d",&n);
	printf("%lld",Matrix_tree());
}

思路 2 2 2

对于懒得打上一种做法的怎么办?
那就用刚才的代码打表找规律。
表如下:
1 = 1 ∗ 1 1=1*1 1=11
5 = 5 ∗ 1 \quad\quad\quad\quad\quad\quad\quad\quad5=5*1 5=51
16 = 4 ∗ 4 16=4*4 16=44
45 = 5 ∗ 3 ∗ 3 \quad\quad\quad\quad\quad\quad\quad\quad45=5*3*3 45=533
121 = 11 ∗ 11 121=11*11 121=1111
320 = 5 ∗ 8 ∗ 8 \quad\quad\quad\quad\quad\quad\quad\quad320=5*8*8 320=588
841 = 29 ∗ 29 841=29*29 841=2929
2205 = 5 ∗ 21 ∗ 21 \quad\quad\quad\quad\quad\quad\quad\quad2205=5*21*21 2205=52121
5776 = 76 ∗ 76 5776=76*76 5776=7676
15125 = 5 ∗ 55 ∗ 55 \quad\quad\quad\quad\quad\quad\quad\quad15125=5*55*55 15125=55555
讲奇偶性分开。
容易看出奇数是某个数的平方,偶数是某个数的平方的 5 5 5倍。
寻找某个数的规律。
发现某个数其实就是 f n = 3 f n − 1 − f n − 2 f_n=3f_{n-1}-f_{n-2} fn=3fn1fn2
最后分类谈论算出对应的值即可。

但是,还有没有更好的规律呢。
细细想想 (题解想想) ,设第 n n n个数的结果为 F n F_n Fn,则有: F n = 3 F n − F n − 2 + 2 F_n=3F_n-F_{n-2}+2 Fn=3FnFn2+2
那就更好实现了。

代码 2 2 2

好久之前的了(懒得改)。

#include<cstdio>
#include<cstring>
	struct node{int a[1001];int l;node(){memset(a,0,sizeof(a));}} f[101];
	int n;
node jia(node x)
{
	x.a[1]+=2;
	for(int i=1;i<=x.l;i++)
	{
		x.a[i+1]+=x.a[i]/10;
		x.a[i]%=10;
	}
	x.l++;
	while(x.l>0&&x.a[x.l]==0)
		x.l--;
	return x;
}
node jian(node x,node y)
{
	node z;
	for(int i=1;i<=x.l;i++)
	{
		z.a[i]=x.a[i]-y.a[i];
		if(z.a[i]<0)
		{
			x.a[i+1]--;
			z.a[i]+=10;
		}
	}
	z.l=x.l;
	while(z.l>0&&z.a[z.l]==0)
		z.l--;
	return z;
}
node cheng(node x)
{
	node y;
	for(int i=1;i<=x.l;i++)
	{
		y.a[i]+=x.a[i]*3;
		y.a[i+1]+=y.a[i]/10;
		y.a[i]%=10;
	}
	y.l=x.l+1;
	while(y.l>0&&y.a[y.l]==0)
		y.l--;
	return y;
}
int main()
{
	scanf("%d",&n);
	f[0].a[1]=0;f[0].l=1;
	f[1].a[1]=1;f[1].l=1;
	for(int i=2;i<=n;i++)
		f[i]=jia(jian(cheng(f[i-1]),f[i-2]));
	for(int i=f[n].l;i>=1;i--)
		printf("%d",f[n].a[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值