zoukankan      html  css  js  c++  java
  • P4921 [MtOI2018]情侣?给我烧了!/P4931 [MtOI2018]情侣?给我烧了!(加强版)

    P4921 [MtOI2018]情侣?给我烧了!

    P4931 [MtOI2018]情侣?给我烧了!(加强版)

    两道好题做了一上午,可见我的组合数学有多差,只会暴力拆式子,于是加强版只好看题解了。。总结一下于是有了这篇题解。

    普通版

    (naive) 的想法就是:钦定 (k) 对情侣,给他们安排座位,两个人可以互换位置,其余人是一个变相的错排:(C_n^kA_n^k2^kf(n-k))(f(k)) 表示 (k) 对人坐 (2k) 个位置不配对的情况数。我发现这个 (f(k)) 我根本不会求就放弃了这个思路。

    之前没见过二项式反演,所以没什么感觉。现在一上来就设俩式子:

    (f(k)) 表示至少 (k) 对情侣和睦

    (g(k)) 表示恰好 (k) 对情侣和睦

    我们要求的是 (g(i),i=0,1,cdots,n)

    显然 (f(k)=sum_{i=k}^{n}inom{n}{k}g(i))

    由二项式反演得 (g(k)=sum_{i=k}^{n}(-1)^{i-k}inom{n}{k}f(i))

    考虑把 (f(k)) 换一种表示(之前设这两个式子的原因其实是因为看到 (f(i)) 好算):

    首先钦定 (k) 对和睦的情侣,然后给他们挑 (k) 个位置,每一对情侣可以互换位置,其余人随便排(这里是“至少”的体现,有可能大于 (k) ,但是不可能小于 (k)

    把上面的文字写成式子就是 (f(k)=C_n^kA_n^k2^k(2n-2k)!)

    (f) 可以一遍 (O(n)) 扫出来,但是 (g) 呢?它要 (O(n^2)) 。怎么办?想了一会感觉没啥别的想法就开始大力拆式子:

    [g(k)=sum_{i=k}^{n}(-1)^{i-k}inom{i}{k}f(i)\ =sum_{i=k}^{n}(-1)^{i-k}dfrac{i!}{k!(i-k)!}dfrac{n!}{i!(n-i)!}dfrac{n!}{(n-i)!}2^k(2n-2i)!\ =sum_{i=0}^{n-k}(-1)^{i}dfrac{1}{k!i!}dfrac{n!}{(n-i-k)!}dfrac{n!}{(n-i-k)!}2^{i+k}(2n-2i-2k)!\ =dfrac{2^kn!^2}{k!}sum_{i=0}^{n-k}dfrac{(-1)^{i}2^i(2n-2k-2i)!}{(n-i-k)!^2i!} ]

    (sum) 后面那个东西对于每一个 (n-k) 是固定的,只有 (O(n)) 种,可以 (O(n^2)) 暴力预处理

    然后就做完了。

    //Orz cyn2006
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef double db;
    #define mkp(x,y) make_pair(x,y)
    #define fi first
    #define se second
    #define pb(x) push_back(x)
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return f?x:-x;
    }
    #define N 2005
    #define mod 998244353
    int n,f[N],g[N],fac[N],ifc[N],pw2[N];
    int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
    void init(){
    	int up=2000;
    	fac[0]=1,pw2[0]=1;
    	for(int i=1;i<=up;++i)fac[i]=1ll*fac[i-1]*i%mod,pw2[i]=(pw2[i-1]<<1)%mod;
    	ifc[up]=qpow(fac[up],mod-2);
    	for(int i=up-1;i>=0;--i)ifc[i]=1ll*ifc[i+1]*(i+1)%mod;
    	up=1000;
    	for(int i=0;i<=up;++i){
    		for(int j=0;j<=i;++j)
    			f[i]=(f[i]+1ll*(j&1?-1:1)*pw2[j]*ifc[j]%mod*fac[2*i-2*j]%mod*ifc[i-j]%mod*ifc[i-j]%mod+mod)%mod;
    	}
    }
    int A(int n,int m){return 1ll*fac[n]*ifc[n-m]%mod;}
    int C(int n,int m){return 1ll*fac[n]*ifc[n-m]%mod*ifc[m]%mod;}
    signed main(){
    	init();
    	for(int T=read();T;--T){
    		n=read();
    		for(int i=0;i<=n;++i)printf("%lld
    ",1ll*pw2[i]*fac[n]%mod*fac[n]%mod*ifc[i]%mod*f[n-i]%mod);
    	}
    }
    

    加强版

    原本以为就是简单版加一个多项式就好了的,那个式子 (sum) 后面那部分显然可以 (NTT) 卷起来。

    当我看到 (nle 5 imes10^6) 之后意识到事情有些不对劲。

    看了题解发现神仙出题人把普通版里最开始写的那个式子的 (f) 推出来了!!!

    技不如人啊,我被吊打了/kk

    (C_n^kA_n^k2^kf(n-k))

    考虑怎么求 (f)

    考虑分类讨论一下:

    选两个人,ta们不是配偶,这样有 (2k*(2k-2)) 种情况,然后考虑ta们配偶的情况

    • 如果强制让ta们配对,相当于在剩下 (k-1) 个位置选一个给ta们,同时ta们还可以交换位置,是 (2(k-1)f(k-2))
    • 如果强制让ta们分开,相当于少了俩人,剩下的人又是一个错排,为 (f(k-1))
      完全不知道第二种情况是怎么看出来的,出题人太神仙了。
      边界 (f(0)=1,f(1)=0)
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef double db;
    #define mkp(x,y) make_pair(x,y)
    #define fi first
    #define se second
    #define pb(x) push_back(x)
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return f?x:-x;
    }
    #define mod 998244353
    #define N 5000005
    int T,n,k;
    int pw2[N],fac[N],ifc[N],f[N];
    int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
    void init(){
    	int up=N-5;
    	pw2[0]=fac[0]=1;
    	for(int i=1;i<=up;++i)fac[i]=1ll*fac[i-1]*i%mod,pw2[i]=(pw2[i-1]<<1)%mod;
    	ifc[up]=qpow(fac[up],mod-2);
    	for(int i=up-1;i>=0;--i)ifc[i]=1ll*ifc[i+1]*(i+1)%mod;
    	f[0]=1,f[1]=0;
    	for(int i=2;i<=up;++i)f[i]=4ll*i*(i-1)%mod*(2ll*(i-1)*f[i-2]%mod+f[i-1])%mod;
    }
    int A(int n,int m){return 1ll*fac[n]*ifc[n-m]%mod;}
    int C(int n,int m){return 1ll*fac[n]*ifc[n-m]%mod*ifc[m]%mod;}
    signed main(){
    	init();
    	for(int T=read();T;--T)
    		n=read(),k=read(),printf("%lld
    ",1ll*A(n,k)*C(n,k)%mod*pw2[k]%mod*f[n-k]%mod);
    }
    
  • 相关阅读:
    MySQL锁(阻塞)
    MySQL锁类型(一致性是非锁定读、自增和外键)
    MySQL锁算法(行锁的三种算法以及解决幻读问题)
    MySQL锁概述
    MySQL锁问题(脏读、不可重复读、幻读)
    MySQL默认隔离级别对应解决的三种问题
    简单动态字符串
    限流
    # SpringBoot自定义线程池
    & 生产环境mysql问题记录
  • 原文地址:https://www.cnblogs.com/zzctommy/p/13931509.html
Copyright © 2011-2022 走看看