zoukankan      html  css  js  c++  java
  • 【洛谷P4831】Scarlet loves WenHuaKe

    题目

    题目链接:https://www.luogu.com.cn/problem/P4831
    Scarlet尝试在(n)(m)列的中国象棋棋盘上放置(2 imes n) 个炮,使得它们互不攻击。
    大家都知道Scarlet沉迷搞事,她想问你有多少个方案。
    对于 (45\%) 的数据,(n,mleq 2000)
    对于剩余 (55\%) 的数据,(n,mleq 10^5,m-nleq 10)

    思路

    这道题完全可以加强为 (n,mleq 10^5)。不需要其他限制。
    显然每行必须有恰好两个棋子。所以我们可以转化为一张二分图,其中左边的点表示行,右边的点表示列。那么左右各有 (2n) 个点。
    显然左边的两个点对应原图的一行。而右边的点可以两个组成一列,或者一个组成一列。考虑枚举右边点两个组成一列的数量 (i)。那么等价于原图中有 (i) 列有两个棋子,(2(n-i)) 列有一个棋子,(m-i-2(n-i)) 列没有棋子。
    (h_i) 表示有 (i) 列有两个棋子的方案数。但是我们无法直接计算,因为拆点之后可能会重复。所以再枚举 (j) 表示有 (j) 个点计算重复了,那么有

    [h_i=frac{1}{2^{n+i}}sum^{i}_{j=0}(-1)^j2^jj!inom{i}{j}inom{n}{j} imes (2(n-i))! ]

    首先任意拆了点的棋子,它的贡献都会被计算两次,而一共有 (2^{n+i}) 个点被拆了,所以要除以 (2^{n+i})。计算重复的 (j) 个点与右边一共有 (2^jj!) 种组合方式;左右拆的点中各选出 (j) 各点;剩余点的匹配数有 ((2(n-i))!) 种。
    通过这一句 十 分 通 顺 的话我们可以得出 (h_i) 的式子。那么设

    [f_i=(-1)^iinom{n}{i}2^i(2(n-i))! ]

    [g_i=frac{1}{(k-i)!} ]

    我们可以得到

    [h_i=frac{i!}{2^{n+i}}sum_{j=1}^{i}f_jg_{i-j} ]

    这一步不难,直接暴力拆式子即可。
    发现模数是十分可爱的 NTT 模数,直接上 NTT 即可。
    最终计算答案就是在 (m) 列中选出 (i) 列放两个棋子,(2(n-i)) 列放一个棋子。

    [ans=sum^{n}_{i=1}inom{m}{i}inom{m-i}{2(n-i)}h_i ]

    时间复杂度 (O(nlog n+m))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=300010,MOD=998244353,G=3,Ginv=332748118;
    int n,m,lim,rev[N];
    ll ans,f[N],g[N],fac[N],X[N],Y[N];
    
    ll fpow(ll x,ll k)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%MOD)
    		if (k&1) ans=ans*x%MOD;
    	return ans;
    }
    
    ll C(int n,int m)
    {
    	return fac[n]*fpow(fac[m],MOD-2)%MOD*fpow(fac[n-m],MOD-2)%MOD;
    }
    
    void NTT(ll *f,int tag,int lim)
    {
    	for (int i=0;i<lim;i++)
    		if (rev[i]<i) swap(f[rev[i]],f[i]);
    	for (int k=1;k<lim;k<<=1)
    	{
    		ll tmp=fpow((tag==1)?G:Ginv,(MOD-1)/(k<<1));
    		for (int i=0;i<lim;i+=(k<<1))
    		{
    			ll w=1;
    			for (int j=0;j<k;j++,w=w*tmp%MOD)
    			{
    				ll x=f[i+j],y=w*f[i+j+k]%MOD;
    				f[i+j]=(x+y)%MOD; f[i+j+k]=(x-y+MOD)%MOD; 
    			}
    		}
    	}
    }
    
    void Fmul(ll *f,ll *g,int lim)
    {
    	memset(X,0,sizeof(X));
    	memset(Y,0,sizeof(Y));
    	for (int i=0;i<lim;i++)
    		X[i]=f[i],Y[i]=g[i];
    	NTT(X,1,lim); NTT(Y,1,lim);
    	for (int i=0;i<lim;i++) X[i]=X[i]*Y[i]%MOD;
    	NTT(X,-1,lim);
    	ll inv=fpow(lim,MOD-2);
    	for (int i=0;i<lim;i++) X[i]=X[i]*inv%MOD;
    	memcpy(f,X,sizeof(X));
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	fac[0]=1;
    	for (int i=1;i<=2*m;i++)
    		fac[i]=fac[i-1]*i%MOD;
    	ll p=1;
    	for (int i=0;i<=n;i++,p=-p)
    	{
    		f[i]=p*C(n,i)*fpow(2,i)%MOD*fac[2*(n-i)]%MOD;
    		g[i]=fpow(fac[i],MOD-2);
    	}
    	lim=1;
    	while (lim<=2*n) lim<<=1;
    	for (int i=0;i<lim;i++)
    		rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
    	Fmul(f,g,lim);
    	for (int i=0;i<=n;i++)
    		if (2*n-i<=m)
    		{
    			f[i]=f[i]*fac[i]%MOD*fpow(2,1LL*(n+i)*(MOD-2))%MOD;
    			ans=(ans+f[i]%MOD*C(m,i)%MOD*C(m-i,2*(n-i)))%MOD;
    		}
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    fastjson(转)
    FastJson
    FastJson处理Map List 对象
    spring-data-redis RedisTemplate操作
    Spring-data-redis:特性与实例(转载)
    Spring Boot 支持多种外部配置方式
    springBoot----@ConditionalOnxxx相关注解总结
    Centos7 install Openstack
    Centos7 install Openstack
    Centos7 Openstack
  • 原文地址:https://www.cnblogs.com/stoorz/p/14292573.html
Copyright © 2011-2022 走看看