luogu
普通版題解:https://www.cnblogs.com/lcxer/p/10876856.html
在普通版里,我們考慮對於\(n\)對情侶,恰好\(k\)對是和諧的方案數是
\[ans[n][k]=\binom{n}{k}A^k_n2^kg(n-k) \]
然而這樣做是\(O(n^2)\)的,瓶頸在於如何快速求出\(g(n-k)\)
之前我們的做法需要用到\(ans\)數組,這樣是無法優化的,我們換一個思路來求\(g\)
假如我們已經確定了\(n-1\)對情侶都是亂序的方案數\(g(n-1)\)
那么\(n\)對情侶亂序可以看做選出\(1\)對情侶是和諧的,然后用這\(1\)對情侶中的一個人與其他亂序的人交換,這樣得出來的一定是\(n\)對情侶亂序的方案,交換的方案一共是\(2*2(n-1)\),然后考慮將新生成的這對座位插到那\(n-1\)對情侶中的方案數為\(n\)
這一部分的總方案數是
\[4n(n-1)*g(n-1) \]
然而這樣算的並不全,還有一種情況考慮不到:如果有兩對情侶都是和諧的,他們之間互換,這種情況之前的方案是考慮不到的
這樣的方案數是多少呢?
除去我們新加的這一組,那么我們就選\(1\)組出來,選出來的方案數是\(n-1\)
這兩組互換的方案數是\(8\)
然后再將這兩組插回去方案數是\(n(n-1)\)
這一部分的總方案數是
\[8n(n-1)(n-1)*g(n-2) \]
可以發現這已經是所有的方案數了,如果我們多選兩組出來,這兩組互換一下就是第一部分的方案了,同理其他的情況都可以轉化為這兩種情況
所以
\[g(n)=4n(n-1)*g(n-1)+8n(n-1)(n-1)*g(n-2) \]
預處理出來就可以\(O(1)\)詢問了
代碼:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=5e6+10,N=5e6,mod=998244353;
int k,T,n,g[maxn],fac[maxn],inv[maxn],d[maxn];
int mul(int x,int y){return 1ll*x*y-1ll*x*y/mod*mod;}
int del(int x,int y){return x-y<0?x-y+mod:x-y;}
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int mi(int a,int b){
int ans=1;while(b){if(b&1)ans=mul(ans,a);b>>=1,a=mul(a,a);}
return ans;
}
int C(int n,int m){return mul(fac[n],mul(inv[m],inv[n-m]));}
int A(int n,int m){return mul(fac[n],inv[n-m]);}
int main()
{
read(T);fac[0]=inv[0]=d[0]=1;
for(rg int i=1;i<=N;i++)fac[i]=mul(fac[i-1],i),d[i]=mul(2,d[i-1]);
inv[N]=mi(fac[N],mod-2);
for(rg int i=N-1;i;i--)inv[i]=mul(inv[i+1],i+1);
g[0]=1;
for(rg int i=1;i<=N;i++)g[i]=add(mul(i,mul(4*i-4,g[i-1])),mul(i,mul(8*(i-1),mul(i-1,g[i-2]))));
while(T--)read(n),read(k),printf("%d\n",mul(C(n,k),mul(A(n,k),mul(d[k],g[n-k]))));
}