[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;
}
本文来自博客园,作者:zhouhuanyi,转载请注明原文链接:https://www.cnblogs.com/zhouhuanyi/p/16983627.html

浙公网安备 33010602011771号