P6475 [NOI Online #2 入门组] 建设城市

P6475 [NOI Online #2 入门组] 建设城市

题目大意

球球是一位建筑师。一天,他收到市长的任务:建设城市。球球打算建造 \(2n\) 座高楼。为了保证城市美观,球球做出了如下计划:

  • 球球喜欢整齐的事物。他希望高楼从左向右排成一行,编号依次为 \(1\sim 2n\)

  • 球球喜欢整数,他要求每座高楼的高度都是正整数。

  • 由于材料限制,高楼的高度无法超过 \(m\)

  • 球球喜欢中间高,两边低的造型。他要求前 \(n\) 座高楼的高度不下降,后 \(n\) 座高楼的高度不上升。

  • 球球打算选两座编号为 \(x,y\) 的高楼作为这座城市的地标。他认为只有当这两座高楼高度相等时,才会让城市变得美观。

球球把自己的想法告诉了市长。市长希望得知所有建设城市的方案数。两种方案不同,当且仅当某座高楼的高度在两个方案中不同。这个问题可难倒了球球。球球找到了你,希望你能帮他算出答案。由于答案可能很大,你只需要给出答案对 \(998244353\) 取模后的结果。

分析

因为有x,y在这里卡着,因此我们首先肯定是要进行分类讨论。

在此之前,我们首先需要解决一个问题,一个序列有k个数,数字的取值范围为[l,r],则若要使序列单调不降/不升的合法序列个数为多少

结论是,\(C(r-l+1+k,r-l)\)

这个推导也很容易,可以将问题等价于,k个相同的球放到r-l+1个不同的盒子中的方案数。

接下来,我们来分类讨论。

x<=n&&y>n

我们枚举[x,y]段的取值为i

  • 第一段[1,x-1],取值范围[1,i]
  • 第二段[x+1,n],取值范围[i,m]
  • 第三段[n+1,y-1],取值范围[i,m]
  • 第四段[y+1,2n],取值范围[1,i]

四段相互独立,利用乘法原理得到。

\[res = C(x-2+i,i-1)*C(m-i+n-x,m-i)*C(m-i+y-(n+1),m-i)*C(i+2*n-y-1,i-1) \]

x<y<=n||n<x<y

则我们可以将[x,y]看成一个城市。

\(res=C(m+n-1,m-1)*C(m+n-(y-x)-1,m-1)\)

Ac_code

#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;

const int N =  2e5 + 10,mod = 998244353;

template<int T>
struct ModInt {
    const static int mod = T;
    int x;
    ModInt(int x = 0) : x(x % mod) {}
    int val() { return x; }
    ModInt operator + (const ModInt &a) const { int x0 = x + a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
    ModInt operator - (const ModInt &a) const { int x0 = x - a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
    ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
    ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
    void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; if (x < 0) x += mod;}
    void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; if (x >= mod) x -= mod;}
    void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
    void operator /= (const ModInt &a) { *this = *this / a; }
    friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
    
    ModInt pow(int n) const {
        ModInt res(1), mul(x);
        while(n){
            if (n & 1) res *= mul;
            mul *= mul;
            n >>= 1;
        }
        return res;
    }
    
    ModInt inv() const {
        int a = x, b = mod, u = 1, v = 0;
        while (b) {
            int t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        if (u < 0) u += mod;
        return u;
    }
    
};
typedef ModInt<mod> mint;

mint fact[N],infact[N];
int m,n,x,y;

void init()
{
    fact[0] = infact[0] = 1;
    for(int i=1;i<N;i++) fact[i] = fact[i-1]*mint(i);
    infact[N-1] = fact[N-1].pow(mod-2);
    for(int i=N-2;i;i--) infact[i] = infact[i+1]*mint(i+1);
}

mint C(int a,int b)
{
    return fact[a]*infact[a-b]*infact[b];
}

int main()
{   
    ios;
    init();
    cin>>m>>n>>x>>y;
    mint ans = 0;
    if(x<=n&&y>n) 
    {
        for(int i=1;i<=m;i++)
            ans += C(x-2+i,i-1)*C(m-i+n-x,m-i)*C(m-i+y-(n+1),m-i)*C(i+2*n-y-1,i-1);
    }
    else ans = C(m+n-1,m-1)*C(m+n-(y-x)-1,m-1);
    cout<<ans<<'\n';
    return 0;
}
posted @ 2022-09-07 17:33  艾特玖  阅读(40)  评论(0)    收藏  举报