题解 P6475 【[NOI Online #2 入门组]建设城市(民间数据)】
如果公式挂了请私信我并到博客查看
前言:
这个人网络爆炸了,于是pj成功爆了0。可恶的土豆服务器(〃>皿<)
由于半年没写过关于组合数的题了,所以有锅别怪我(
正文开始
前置知识:快速幂,组合数学(初赛时有讲),逆元。
首先我们设一个函数
然后由插板法可得
接下来就开始狂废脑子了……
对于答案的统计,需要分情况讨论
-
x<=n,y>n
对于这种情况我们可以做一个图示:
在此时,这个序列便分为四个区间:
通过枚举
-
n<x<y$或$x<y<=n
图示:
注意到
又根据乘法原理,我们可以
完了?完了。
似乎忘了什么……
对了:
如何O(1) 计算f 函数啊!!!∑(゚Д゚ノ)ノ
我们之前提到过
所以求
而众所周知,
所以
我们现在剩下的任务就是求
实在不知道逆元的到这来
接下来介绍两种求逆元的方法:
- 无需理解,会逆元就行
学过都知道,一个数
所以在循环的时候,直接每次利用求出的
时间复杂度
代码
void prework()
{
fac[0]=inv[0]=1;
for(int i=1;i<=200000;i++)fac[i]=(fac[i-1]*i)%mod;
for(int i=1;i<=200000;i++)inv[i]=quick(fac[i],mod-2);
}
- 速度快,但需要进行理解
首先我们还需要求出
然后求出
最后后从后往前循环,每次求
证明?
时间复杂度为
代码
void prework()
{
fac[0]=inv[0]=1;
for(int i=1;i<=200000;i++)fac[i]=(fac[i-1]*i)%mod;
inv[200000]=quick(fac[200000],mod-2);
for(int i=199999;i;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
然后调用
ll f(int n,int m){return fac[n+m-1]*inv[n]%mod*inv[m-1]%mod;}
Final Code
#include<iostream>
#include<cstdio>
#define mod 998244353
#define ll long long
using namespace std;
ll fac[200001],inv[200001],n,m,x,y,ans;
ll quick(ll a,ll b)
{
ll xx=a,yy=1;
while(b)
{
if(b&1) yy=yy*xx%mod;
xx=xx*xx%mod;
b/=2;
}
return yy;
}
ll f(int nn,int mm){return fac[mm+nn-1]*inv[nn]%mod*inv[mm-1]%mod;}
int main()
{
fac[0]=inv[0]=1;
for(int i=1;i<=200000;i++)fac[i]=(fac[i-1]*i)%mod;
inv[200000]=quick(fac[200000],mod-2);
for(int i=199999;i;i--)inv[i]=inv[i+1]*(i+1)%mod;
cin>>m>>n>>x>>y;
if(x<=n&&y>n)
for(int i=1;i<=m;i++)
ans=(ans+f(x-1,i)*f(n-x,m-i+1)%mod*f(2*n-y,i)%mod*f(y-n-1,m-i+1))%mod;
else ans=f(n,m)*f(n+x-y,m)%mod;
cout<<ans;
}