[ARC160D] Mahjong

Solution#

首先判掉 km 时显然无解的情况。

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

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

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

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

ans=i=0nk+1(1)i(nk+1i)(mkik+2nk2nk)

暴力计算组合数,时间复杂度 O(n2logmod)


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;
}
posted @   MrcFrst  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示
主题色彩