• AGC036C GP 2


    有个一开始全(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)的条件:

    1. (sum y_i=m)
    2. (a_i-2y_i+y_ile m)
    3. (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)

    然后就可以计数啦。列出式子:

    [F=sum_{i=0}^mx^{2i},G=sum_{i=0}^{m-1}x^{2i+1}=x(F-x^{2m})\ ans=[x^{3n}y^m]frac{1}{1-y}(F+Gy)^n\ ]

    经过简单的推式子过程,就可以得到(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;
    }
    
  • 相关阅读:
    25个完美的Opencart模板,让顾客无法抗拒!
    来自极客标签10款最新设计素材-系列十
    Creating Contextual Menus创建上下文菜单
    java解惑之常常忘记的事
    java 泛型实例详解(普通泛型、 通配符、 泛型接口)
    Java 泛型、通配符? 解惑
    Java中public,private,protected,和默认的区别
    windows 环境下dos 命令符下进D盘(非c盘系统盘)根目录
    I/O流之--转换流:InputStreamReader 和InputStreamWriter
    java 代码执行cmd 返回值异常 (关于JAVA Project.waitfor()返回值是1)
  • 原文地址:https://www.cnblogs.com/jz-597/p/14742554.html
Copyright © 2020-2023  润新知