1. 程式人生 > >2018 Multi-University Training Contest 8 1001 Character Encoding【容斥】

2018 Multi-University Training Contest 8 1001 Character Encoding【容斥】

題意:每個數字的取值範圍為0到n-1,共有m個數字,求總和為k的方案數。

因為k的上限為1e5,那麼我們不妨把k看做k個1,然後通過m-1個隔板來分割出m個數字。但是這麼做會有一個問題,那就是數字可能為0。那麼不妨將數字的取值範圍改成1-n,m個數所以k要相應的變成k+m,那麼就可以使用隔板法了。

先無限制的求一下總數:Cm+k1m1

如果我們現在拿出來n個1,將剩下的數分割成m個數,那麼把這n個1放到任何位置上,都是至少有一個數字超出限制的。

也就是:Cm+k1m1Cm1Cm+k

1nm1+Cm2Cm+k12nm1

如此反覆直到不合法結束,到x結束也就是 到無法滿足至少x個數超過限制時結束。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;
#define  LL long long

const LL N = 3e5 + 10;
const LL MAXN = 3e5;
const LL mod = 998244353
; LL fac[N]; LL inv[N]; LL qkm(LL base,LL mi) { LL ans=1; while(mi){ if(mi&1) ans=ans*base%mod; base=base*base%mod; mi>>=1; } return ans; } LL C(LL n,LL m) { if(m>n) return 0; return fac[n]*inv[m]%mod*inv[n-m]%mod; } int main() { fac[0
]=fac[1]=1; for(LL i=2;i<=MAXN;i++)fac[i]=fac[i-1]*i%mod; inv[MAXN]=qkm(fac[MAXN],mod-2); for(LL i=MAXN-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod; LL T; cin>>T; while(T--){ LL n,m,k; cin>>n>>m>>k; LL ans=C(k+m-1,m-1); LL p=1; LL di = k+m-1-n; LL num=1; while(di>=m-1){ if(p){ ans=ans-C(m,num)*C(di,m-1)%mod+mod; ans%=mod; }else{ ans=ans+C(m,num)*C(di,m-1)%mod; ans%=mod; } p=p^1; num++; di-=n; } cout<<ans<<endl; } return 0; }