[NOIP模拟测试]:建设城市(city)(组合数学+容斥)

题目传送门(内部题8)


输入格式

一行三个整数$n,m,k$。


输出格式

一行一个整数表示答案。对$998244353$取模。


样例

样例输入

3 7 3

样例输出

6


数据范围与提示

对于10%的数据,$1\leqslant n,m,k\leqslant 10$。
对于40%的数据,$1\leqslant n,m,k\leqslant 1,000$。
对于70%的数据,$1\leqslant n,m,k\leqslant {10}^5$。
对于100%的数据,$1\leqslant n\leqslant {10}^9$,$1\leqslant n,k\leqslant {10}^7$。


题解

$20%$算法:

如果$n>m$或$n<m\times k$那么一定没有方案,直接$puts("0");$即可。

时间复杂度:$\Theta(1)$。

期望的分:$0$分。

实际的分:$20$分。

$10%$算法:

爆搜,枚举所有情况即可。

时间复杂度:$\Theta(n^k)$。

期望的分:$10$分。

实际的分:$10$分。

$40%$算法:

考虑$DP$,设$dp[i][j]$为到第$i$个城市,一共用了$j$个建设队的方案数。

那么可以列出状态转移方程:$dp[i][j]=\sum \limits_{k=1}^{\min(k,m)}dp[i-1][j-k]$。

时间复杂度:$\Theta(n^3)$。

期望的分:$40$分。

实际的分:$40$分。

$100%$算法:

考虑容斥,挡板法。

这个问题可以转化为,在$m$个物品中插入$n-1$个挡板,挡板不能插在一起,那么方案数就是$C_{m-1}^{n-1}$。

现在需要减去不合法的方案数,设至少有$i$个城市不合法,那么方案数就是$C_n^i\times C_{m-i\times k-1}^{n-1}$,可以理解为,我先将那$i\times k$个不合法的扔掉,再在$m-i\times k$里面选合法的即可。

利用容斥统计答案即可。

时间复杂度:$\Theta(m)$。

期望的分:$100$分。

实际的分:$100$分。


代码时刻

$20%$算法:

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	if(n>m||n*k<m)puts("0");
	return 0;
}

$40%$算法:

#include<bits/stdc++.h>
using namespace std;
int dp[7000][7000];
int main()
{
	int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;i++)dp[1][i]=1;
	for(int i=2;i<=n;i++)
		for(int j=i;j<=m;j++)
			for(int l=1;l<=min(k,m);l++)
				dp[i][j]=(dp[i][j]+dp[i-1][j-l])%998244353;
	cout<<dp[n][m]<<endl;
	return 0;
}

$100%$算法:

#include<bits/stdc++.h>
using namespace std;
long long n,m,k;
long long jc[100000001],qsm[100000001];
long long ans;
long long qpow(long long x,long long y)
{
	long long res=1;
	while(y)
	{
		if(y&1)res=res*x%998244353;
		x=x*x%998244353;
		y>>=1;
	}
	return res;
}
void pre_work()
{
    jc[0]=1;
    for(int i=1;i<=m;i++)
        jc[i]=1LL*jc[i-1]*i%998244353;
    for(int i=0;i<=m;i++)
        qsm[i]=qpow(jc[i],998244351)%998244353;
}
long long cm(long long x,long long y)
{
    return jc[x]*qsm[y]%998244353*qsm[x-y]%998244353;
}
long long lucas(long long x,long long y)
{
    if(!y)return 1;
    return cm(x%998244353,y%998244353)*lucas(x/998244353,y/998244353)%998244353;
}
int main()
{
	scanf("%lld%lld%lld",&n,&m,&k);
	if(n>m||n*k<m){puts("0");return 0;}
	pre_work();
	long long flag=-1;
	ans=jc[m-1]*qsm[n-1]%998244353*qsm[m-n]%998244353;
	for(int i=1;i<=n;i++)
	{
		if(m-i*k<n)continue;
		ans=(ans+flag*lucas(n,i)%998244353*lucas(m-i*k-1,n-1)+998244353)%998244353;
		flag=-flag;
	}
	printf("%lld",(ans+998244353)%998244353);
	return 0;
}

rp++

猜你喜欢

转载自www.cnblogs.com/wzc521/p/11329755.html