Loading

[CSGRound3]仙人掌

链接:https://www.luogu.com.cn/problem/P6017

题目描述:定义无向图的度数序列,度数序列的长度为图中点的个数,度数序列中第\(i\)是图中编号为\(i\)的度数。对于所有\(n\)个点\(m\)条边的边仙人掌,其不同的度数序列有多少种。

题解:考虑求出\(n\)个点的仙人掌的边数上界\(f(n)\),可以发现:

\(n\)为偶数时,\(f(n)=\frac{3}{2}\times n-2\)

\(n\)为奇数时,\(f(n)=\frac{3}{2}\times (n-1)\)

\(m\)缩小到了\(4000\)以下。可以发现,当节点的度数为偶数时,该度数序列合法。

当节点的度数有奇数时,可以将奇数点合并缩成偶数点,但度数唯一的点不能和
度数唯一的点缩成一个点,因为这样会使图不连通。

可以得到,合法序列只与\(t_{1}\),\(t_{odd}\)有关(其中\(t_{1}\)表示度数为一的点的个数,\(t_{odd}\)表示度数为大于一的奇数的点的个数)。可以枚举\(t_{1}\)\(t_{odd}\),则此时缩得的图点数为\(n_{0}=n-t_{1}\),边数为\(m_{0}=m-\frac{t_{1}+t_{odd}}{2}\)。则该图的贡献为\(C(m-1,n-1)\times C(n_{0},t_{odd}) \times C(n,t_{1})\)

#include<iostream>
#define mod 998244353
using namespace std;
long long fac[4001],invfac[4001],ans;
int maxcost(int x)
{
	if (x%2==0)
		return 3*x/2-2;
	else
		return 3*(x-1)/2;
}
long long fast_pow(long long a,int b)
{
	if (b==0)
		return 1;
	if (b&1)
		return fast_pow(a*a%mod,b/2)*a%mod;
	else
		return fast_pow(a*a%mod,b/2);
}
long long C(int a,int b)
{
	if (a==-1&&b==0)
		return 0;
	if (a==-1&&b==-1)
		return 1;
	if (a==0&&b==-1)
		return 1;
	if (a>b)
		return 0;
	return fac[b]*invfac[a]%mod*invfac[b-a]%mod;
}
int main()
{
	int n,m,n0,m0,x,y,t;
	fac[0]=1;
	for (int i=1;i<=4000;++i)
		fac[i]=fac[i-1]*i%mod;
	invfac[4000]=fast_pow(fac[4000],mod-2);
	for (int i=3999;i>=0;--i)
		invfac[i]=invfac[i+1]*(i+1)%mod;
	cin>>t;
	while (t--)
	{
		ans=0;
		cin>>n>>m;
		if (m>maxcost(n)&&m<n-1)
		{
			cout<<0<<endl;
			return 0;
		}
		for (int i=0;i<=n;++i)
			for (int j=0;i+j<=n;++j)
				if (i+3*j<=2*m&&(i+j)%2==0)
				{
					if (i>j)
					{
						if (m-i>maxcost(n-i)||m-i<n-i-1)
							continue;
					}
					else
					{
						if (m-(i+j)/2>maxcost(n-(i+j)/2)||m-(i+j)/2<n-(i+j)/2-1)
							continue;
					}
					n0=n-i;
					m0=m-(i+j)/2;
					ans=(ans+C(n0-1,m0-1)*C(j,n0)%mod*C(i,n)%mod)%mod;
				}
		cout<<ans<<endl;
	}
	return 0;
}
posted @ 2022-12-14 21:27  zhouhuanyi  阅读(63)  评论(0)    收藏  举报