[ARC160D] Mahjong

发布时间 2024-01-12 09:53:36作者: MrcFrst

Solution

首先判掉 \(k\nmid m\) 时显然无解的情况。

然后考虑倒着做,往序列上加,那么有显然的 \(dp\),记 \(f_{i,j}\) 表示考虑前 \(i\) 个位置,总共做了 \(j\) 次操作的方案数,转移的时候将区间加操作钦定在最后一个位置统计,暴力枚举操作的数量即可转移。

但发现会算重,具体来说,在同一个末位置进行 \(k\) 次区间加操作等效于做 \(k\) 次单点加。所以我们钦定同一位置只能做最多 \(k-1\) 次区间加操作,这样就不会算重了。转移的时候枚举两种操作即可转移,用前缀和优化之类的操作可以优化复杂度,但是复杂度仍然很炸,因为与操作数相关。

考虑到区间加操作有 \(k-1\) 的次数限制,而单点加操作没有限制,于是可以把两种操作分开考虑,有 \(n-k+1\) 个位置进行区间加,\(n\) 个位置进行单电加,相当于限制前者每个数 \(\lt k\),后者随便填,这 \(2n-k+1\) 个数的总和为操作数 \(\frac{m}{k}\)

枚举 \(i\) 个位置一定不合法,其他随便填(可以填 \(0\)),就可以把 \(\lt k\) 的限制容斥掉,然后用插板法即可。

\[ans=\sum_{i=0}^{n-k+1}{(-1)^i {n-k+1\choose i}{\frac{m}{k}-ik+2n-k\choose 2n-k}} \]

暴力计算组合数,时间复杂度 \(O(n^2\log mod)\)


Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define il inline
#define re register
const int N=2020,mod=998244353;
int n,m,k,ans;
il int read(){
    re int x=0;re char c=getchar(),f=0;
    while(c<'0'||c>'9')f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f?-x:x;
}
il int Pow(int a,int b){
    re int res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod,b>>=1;
    }
    return res;
}
il int C(int n,int m){
    if(n<0||m<0||n<m)return 0;
    re int res=1;
    for(re int i=n;i>n-m;i--)res=res*(i%mod)%mod;
    for(re int i=1;i<=m;i++)res=res*Pow(i,mod-2)%mod;
    return res;
}
signed main(){
    n=read(),m=read(),k=read();
    if(m%k)return puts("0"),0;
    m/=k;
    for(re int i=0;i<=n-k+1;i++)
        ans=(ans+((i&1)?mod-1:1)*C(n-k+1,i)%mod*C(m-i*k+(n<<1)-k,(n<<1)-k))%mod;
    cout<<ans;
    return 0;
}