AtCoder AGC036C GP 2 (组合计数)
題目鏈接
https://atcoder.jp/contests/agc036/tasks/agc036_c
題解
終于有時間補agc036的題了。
這題其實不難的來著……我太菜了考場上沒想出來
首先轉化一下題目: 一個序列可以被按題目的操作方式生成當且僅當它長度為\(N\), 總和為\(3M\), 且最大數不超過\(2M\), 奇數的個數不超過\(M\).
必要性顯然,充分性歸納易證。
然后考慮怎么計數: 先不考慮第二個條件,定義\(f(n,m,k)\)表示長度為\(n\)總和為\(m\)奇數不超過\(k\)個的方案數,那么枚舉奇數的個數\(i\), 剩下的偶數和為\(m-1\), 有\(f(n,m,k)=\sum^{k}_{i\equiv m(\mod 2)}{n\choose i}{\frac{m-i}{2}+n-1\choose n-1}\).
考慮第二個條件,補集轉化,最大數大于\(2M\)意味著剩下的所有數和小于\(M\), 那么不要把和式寫出來然后無腦推式子!固定下最大的數的位置\(1\),給第一個數減去\(2M\) (這是個偶數所以不影響奇數那個條件),就是要求\(N\)個數和為\(M\), 第一個數大于\(0\),一共有不超過\(M\)個奇數的方案數。這個因為有奇數個數的限制所以枚舉很麻煩,那就再補集轉化!轉化為\((N-1)\)個數和為\(M\)且奇數不超過\(M\)個。
因此最后答案就是\(f(N,3M,M)-N(f(N,M,M)-f(N-1,M,M))\).
時間復雜度\(O(N+M)\).
代碼
#include<cstdio> #include<cstdlib> #include<cstring> #include<cassert> #include<iostream> #define llong long long using namespace std;inline int read() {int x=0; bool f=1; char c=getchar();for(;!isdigit(c);c=getchar()) if(c=='-') f=0;for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');if(f) return x;return -x; }const int N = 2e6; const int P = 998244353; llong fact[N+3],finv[N+3];llong quickpow(llong x,llong y) {llong cur = x,ret = 1ll;for(int i=0; y; i++){if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}cur = cur*cur%P;}return ret; } llong comb(llong x,llong y) {return x<0||y<0||x<y ? 0ll : fact[x]*finv[y]%P*finv[x-y]%P;}llong calc(llong n,llong m,llong k) {llong ret = 0ll;for(int i=0; i<=k; i++){if((m-i)&1) continue;llong tmp = comb(n,i)*comb(((m-i)>>1)+n-1,n-1)%P;ret = (ret+tmp)%P;} // printf("calc %lld %lld %lld=%lld\n",n,m,k,ret);return ret; }int n,m;int main() {fact[0] = 1ll; for(int i=1; i<=N; i++) fact[i] = fact[i-1]*i%P;finv[N] = quickpow(fact[N],P-2); for(int i=N-1; i>=0; i--) finv[i] = finv[i+1]*(i+1)%P;scanf("%d%d",&n,&m);llong ans = calc(n,3*m,m);ans = (ans-n*(calc(n,m,m)-calc(n-1,m,m)+P)%P+P)%P;printf("%lld\n",ans);return 0; }總結
以上是生活随笔為你收集整理的AtCoder AGC036C GP 2 (组合计数)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Luogu P4708 画画 (Burn
- 下一篇: AtCoder AGC036D Nega