NOI Online #2 入门组 T3 建设城市

原题链接

简洁题意:

给定一个 n n n,构造一个每个数都不大于 m m m,前 n n n个不下降,后 n n n个不上升,且 a x = a y a_x=a_y ax=ay的序列。注意,没有说 n n n是最高楼。

分析:

这个题可以发现,如果没有 a x = a y a_x=a_y ax=ay的要求就是分成前后两边,前面 n n n个不下降, m m m种高度看成盒子,要把 n n n个楼放进去。可以给每个盒子手动放一个球,就变成了插板法: C n + m − 1 m − 1 C_{n+m-1}^{m-1} Cn+m1m1就是答案。为了方便,后面的 F n m F_{n}^{m} Fnm表示 m m m种高度放 n n n个楼,即 C n + m − 1 m − 1 C_{n+m-1}^{m-1} Cn+m1m1。回到正题,我们这样放了前 n n n个楼,后面不上升的还有 n n n个楼,还是 m m m种高度可以选,则是 F n m F_{n}^{m} Fnm,两边的方案乘法原理乘起来即可。
考虑加上限制条件 a x = a y a_x=a_y ax=ay。第一种情况,分别处于 n n n的两边。则可以枚举它们两个的高度 h h h,分成四部分: ( 1 , x − 1 ) ( x + 1 , n ) ( n + 1 , y − 1 ) ( y + 1 , 2 ∗ n ) (1,x-1)(x+1,n)(n+1,y-1)(y+1,2*n) (1,x1)(x+1,n)(n+1,y1)(y+1,2n),方案数根据乘法原理是 F x − 1 h × F n − x m − h + 1 × F y − n − 1 m − h + 1 × F 2 × n − y h F_{x-1}^{h}\times F_{n-x}^{m-h+1}\times F_{y-n-1}^{m-h+1}\times F_{2\times n-y}^{h} Fx1h×Fnxmh+1×Fyn1mh+1×F2×nyh
那么考虑两个在同一边,则有一边是没有任何限制的, F n m F_n^m Fnm,另外一边不难发现, x x x y y y中间的楼都是等于 x x x y y y的。所以,把在外面的楼进行计算,把这些相同的也丢出去算,即 F n − x + y m F_{n-x+y}^m Fnx+ym,这样就相当于把这些相同的压缩成一个点进行计算。
最后在考虑 C C C如何计算。因为题目要取模,所以不能用除法,于是只能乘逆元代替除法。则费马小定理出场了:

如果p是一个质数,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1(modp) ap11(modp)

因此,有 a × a p − 2 ≡ 1 ( m o d p ) a\times a^{p-2}\equiv 1(modp) a×ap21(modp),所以 a p − 2 a^{p-2} ap2就是 a a a的逆元。我们这个题模数刚好是质数,于是我们这个题就可以求出逆元来代替除法。因为这个题会算多次的 C C C,所以在前面会先初始化阶乘以及阶乘的逆元。
那么阶乘的逆元怎么求呢?其实也可以递推。 f i = f i + 1 × ( i + 1 ) f_i=f_{i+1}\times (i+1) fi=fi+1×(i+1),这个我就不证明了。所以,只用对最后一个计算快速幂后面递推计算即可。
最后, C C C怎么求呢? C n m = n ! ÷ m ! ÷ ( n − m ) ! C_n^m=n!\div m!\div (n-m)! Cnm=n!÷m!÷(nm)!
这个题预处理是 Θ ( n ) \Theta(n) Θ(n)的,后面枚举了一下高度,时间复杂度 Θ ( m ) \Theta(m) Θ(m),但是如果是另一种情况时间复杂度又是 Θ ( 1 ) \Theta(1) Θ(1)的,所以时间复杂度是 O ( n + m ) O(n+m) O(n+m)的。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN=2e5+4,P=998244353;
ll m,n,x,y,ans,f1[NN],f2[NN];
ll ksm(ll a,ll b)
{
    
    
	ll sum=1;
	while(b)
	{
    
    
		if(b&1)
			sum=sum*a%P;
		a=a*a%P;
		b>>=1;
	}
	return sum;
}
ll C(ll m,ll n)
{
    
    
	return f1[n+m-1]*f2[n]%P*f2[m-1]%P;
}
int main()
{
    
    
	scanf("%d%d%d%d",&m,&n,&x,&y);
	f1[0]=1;
	for(int i=1;i<=m+n;i++)
		f1[i]=f1[i-1]*i%P;
	f2[m+n]=ksm(f1[m+n],P-2);
	for(int i=m+n-1;i>=0;i--)
		f2[i]=f2[i+1]*(i+1)%P;
	if(x<=n&&y>n)
		for(int i=1;i<=m;i++)
			ans=(ans+C(i,x-1)*C(m-i+1,n-x)%P*C(m-i+1,y-n-1)%P*C(i,2*n-y))%P;
	else
		ans=C(m,n)*C(m,n+x-y)%P;
	printf("%d",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44043668/article/details/108814466