【组合数学】AGC036C - GP 2
找性质的能力不行
Problem Statement
We have a sequence of $N$ integers: $x=(x_0,x_1,\cdots,x_{N−1})$. Initially, $x_i=0$ for each $i (0≤i≤N−1)$.
Snuke will perform the following operation exactly $M$ times:
Choose two distinct indices $i,j (0≤i,j≤N−1, i≠j)$. Then, replace $x_i$ with $x_i+2$ and $x_j$ with $x_j+1$.
Find the number of different sequences that can result after $M$ operations. Since it can be enormous, compute the count modulo $998244353$.
Constraints
- $2≤N≤10^6$
- $1≤M≤5×10^5$
- All values in input are integers.
题目分析
这类题的一般套路是把一些奇奇怪怪的操作转化成局部量守恒;或者是操作对一些特殊量的影响有什么限制。
这里首先能够看出限制一:$\forall a_i \le 2m$.
再来考察一下操作有什么特性:在两个位置$i,j$分别加上$2,1$也就是把$j$的奇偶性翻转;$i$的奇偶性不影响。初始每个位置都为偶。这说明全部操作只有$m$次翻转奇偶性的机会,由此得到限制二:$\sum\limits [x_i为奇数]\le m$.
这两个限制的必要性上面已经论述了,充分性可以通过对$m$归纳得到。
现在问题变成了:
给定$n,m$,求长度为$n$的可空数列$\{x_i\}$满足$[\sum x_i=3m ]\and [\forall x_i \le 2m ]\and [\sum\limits [x_i为奇数]\le m]$的个数。
第一条限制很好处理,问题主要是第二条限制。
首先来考虑单纯满足第二条限制的数列个数,不妨记$f(n,m,k)$表示把$m$分为$n$组非空、且奇数个数$\le k$的方案数。那么这里有$f(n,3m,m)=\sum\limits _{p=1,p≡3m\pmod 2}^n {\frac{3m-p}{2}+n-1\choose n-1}{n \choose p}$,其中$p$是枚举的奇数个数。
容斥地考虑,现在要计算的另一部分问题应是$[\exists x_i > 2m ]\and [\sum\limits [x_i为奇数]\le m]$,也即:枚举最大数位置$pos$,把$m$分为$n$组非空、奇数个数$\le k$、且$x_{pos}$非空的方案数。
这里的“某一位置非空”就是很经典的问题了,可以先用$m-1$构造非空数列、再将$1$加到$x_{pos}$上;也可以再容斥一次,用$m$构造$n$的方案减去用$m$构造$n-1$的方案,意即此位置为零不占用$m$.
1 #include<bits/stdc++.h> 2 #define MO 998244353 3 typedef long long ll; 4 const int maxn = 3000000; 5 6 ll n,m,ans; 7 ll inv[maxn],fac[maxn]; 8 9 ll C(ll n, ll m) 10 { 11 if (n < m) return 0; 12 return fac[n]*inv[m]%MO*inv[n-m]%MO; 13 } 14 ll f(ll n, ll m, ll k) 15 { 16 ll ret = 0; 17 for (ll p=0; p<=k; p++) 18 if (((m-p)&1)==0){ 19 ret = (ret+C((m-p)/2ll+n-1, n-1)*C(n, p)%MO)%MO; 20 } 21 return ret; 22 } 23 int main() 24 {27 scanf("%lld%lld",&n,&m); 28 fac[0] = inv[0] = inv[1] = 1; 29 for (int i=2; i<=n+3*m; i++) 30 inv[i] = (MO-1ll*MO/i*inv[MO%i]%MO); 31 for (int i=1; i<=n+3*m; i++) 32 inv[i] = inv[i-1]*inv[i]%MO, fac[i] = fac[i-1]*i%MO; 33 // ans = (f(n, 3*m, m)-n*(f(n, m, m)-f(n-1, m, m))%MO+MO)%MO; 34 ans = (f(n, 3*m, m)-n*(f(n, m-1, m))%MO+MO)%MO; 35 printf("%lld\n",ans); 36 return 0; 37 }
END