有个一开始全(0)的数组,每次选择两个不同的位置,分别(+1)和(+2)。
问操作恰好(m)次之后可能形成的不同的数组的方案数。
(nle 10^6,mle 5*10^5)
考虑最终形成的数组为(a_i)。如果能够将其表示成(x_i+2y_i),那么有:(sum x_i=sum y_i=m)。
分析怎样的((x_i,y_i))有解:相当于每次选择(i eq j),使得(x_i,y_j)各减一,最终能都减到(0)。
显然的必要条件:(x_ile sum_{j eq i}y_j=m-y_i)。于是有(x_i+y_ile m)。可证这也是充分条件:
忽略所有(x_i=y_i=0)的点。
边界情况:(n=2)。此时(x_1+y_1=x_2+y_2=m),因为(x_1+x_2=y_1+y_2=m),可以推出(x_1=y_2,x_2=y_1),有解。
找到个(x_i+y_i)最大的,对于(x_i,y_i)中的较大者,找到(y_j)或(x_j)((j eq i)),两者各减一。(一定能找到:不妨设(x_ige y_i),如果找不到(y_j>0),则(y_i=m),此时(x_i+y_i>m))
因为(x_i+y_i=m)不超过两个(否则总和大于(2m)),所以搞完之后仍然满足(x_i'+y_i'le m')。
归纳得证。
接下来问题是对于(a_i)找到合适的(y_i)。首先有(sum a_i=3m),然后关于(y_i)的条件:
- (sum y_i=m)
- (a_i-2y_i+y_ile m)
- (2y_ile a_i)
可得(max(a_i-m,0)le y_ile lfloorfrac{a_i}{2} floor)。然后得到(sum y_i)的上下界,从而(sum max(a_i-m,0)le mle sumlfloorfrac{a_i}{2} floor)。
整理得(sum a_i=3m,sum (a_imod 2)le m,sum max(a_i-m,0)le m),充分必要。
继续分析:当满足(sum a_i=3m)时,找到最大值次大值(fir,sec),那么第三条限制即(max(fir-m,0)+max(sec-m,0)le m),将(max)拆开,发现其等价于(firle 2m)。所以把第三个条件换成(max a_ile 2m)。
然后就可以计数啦。列出式子:
经过简单的推式子过程,就可以得到(O(n+m))的做法。
using namespace std;
#include <bits/stdc++.h>
#define N 1000005
#define mo 998244353
#define ll long long
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int n,m;
ll fac[N*3],ifac[N*3];
void initC(int n){
fac[0]=1;
for (int i=1;i<=n;++i)
fac[i]=fac[i-1]*i%mo;
ifac[n]=qpow(fac[n]);
for (int i=n-1;i>=0;--i)
ifac[i]=ifac[i+1]*(i+1)%mo;
}
ll C(int m,int n){
if (m<n) return 0;
return fac[m]*ifac[n]%mo*ifac[m-n]%mo;
}
ll calc(int n,int k){
if (k&1) return 0;
k>>=1;
return (C(k+n-1,n-1)-n*C(k-(m+1)+n-1,n-1))%mo;
}
int main(){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
initC(n+m*3/2);
ll ans=0;
for (int j=0;j<=min(n,m);++j)
ans=(ans+C(n,j)*(calc(n,3*m-j)-calc(n-1,m-j)*j%mo))%mo;
ans=(ans+mo)%mo;
printf("%lld
",ans);
return 0;
}