题解 abc225_h Social Distance 2
Description
Solution
首先考虑不区分剩下的 \(m-k\) 个人 , 只考虑他们的位置 , 最后给答案乘上 \((m-k)!\) 即可 .
考虑计算三个值 ,
- 有 \(len\) 个连续的椅子 , 第一个椅子之前和最后一个椅子之后都有人 , 有 \(k\) 个人来坐 , 所有方案的权值和 .
- 有 \(len\) 个连续的椅子 , 第一个椅子之前有人 , 有 \(k\) 个人来坐 , 所有方案的权值和 .
- 有 \(len\) 个连续的椅子 , 有 \(k\) 个人来坐 , 所有方案的权值和 .
先计算第一个
不难发现该式就是把 \(len+1\) 拆分为 \(k+1\) 个有顺序的正整数 , 这些正整数的乘积和 .
现在计算把 \(n\) 拆分为 \(k\) 个有顺序的正整数
那么可以直接写出答案的生成函数 \(F(x)=(\sum\limits_{i}ix^i)^k\),
要求的答案即为 \(F(x)[x^n]\)
考虑写出 \(\sum\limits_{i}ix^i\) 的封闭形式
\(\begin{aligned}\sum\limits_iix^i&=x\sum\limits_iix^{i-1}\\&=x\sum\limits_i(x^i)'\\&=x(\frac{1}{1-x})'\\&=\frac{x}{(1-x)^2}\end{aligned}\)
那么有 \(\displaystyle F(x)=(\frac{x}{(1-x)^2})^k=\frac{x^k}{(1-x)^{2k}}\)
那么答案就是 \(\displaystyle F(x)[x^n]=\frac{x^k}{(1-x)^{2k}}[x^n]=\frac{1}{(1-x)^{2k}}[x^{n-k}]\)
而 \(\displaystyle \frac{1}{(1-x)^{2k}}=\sum\limits_i\binom{i+2k-1}{i}x^i\)
故答案为 \(\displaystyle \binom{n+k-1}{n-k}\)
那么第一个值即为 \(\displaystyle \binom{len+k+1}{2k+1}\)
现在计算第二个值
考虑枚举最后一个人的位置 \(i\), 那么剩下的情况变为了第一个值 .
即 \(\displaystyle\sum\limits_{i=k}^{len}\binom{i-1+k-1+1}{2(k-1)+1}=\sum\limits_{i=k}^{len}\binom{i+k-1}{2k-1}=\binom{len+k}{2k}\)
现在计算第三个值
考虑枚举最后序列的长度 , 那么剩下的情况也变为了第一个值 .
即
\(\displaystyle\begin{aligned}&\sum\limits_{i=k}^{len}(len-i+1)\binom{i-2+k-2+1}{2(k-2)+1}\\&=\sum\limits_{i=k}^{len}(len-i+1)\binom{i+k-3}{2k-3}\\&=\sum\limits_{i=0}^{len-k}(len-i-k+1)\binom{i+2k-3}{2k-3}\\&=(len-k+1)\sum\limits_{i=0}^{len-k}\binom{i+2k-3}{2k-3}-\sum\limits_{i=0}^{len-k}i\binom{i+2k-3}{2k-3}\\&=(len-k+1)\binom{len+k-2}{2k-2}-(2k-2)\sum\limits_{i=0}^{len-k}\binom{i+2k-3}{2k-2}\\&=(len-k+1)\binom{len+k-2}{2k-2}-(2k-2)\binom{len+k-2}{2k-1}\\&=(len-k+1)\frac{(len+k-2)!}{(2k-2)!(len-k)!}-(2k-2)\frac{(len+k-2)!}{(2k-1)!(len-k-1)!}\\&=(len-k+1)(2k-1)\frac{(len+k-2)!}{(2k-1)!(len-k)!}-(2k-2)(len-k)\frac{(len+k-2)!}{(2k-1)!(len-k)!}\\&=\frac{(len+k-2)!}{(2k-1)!(len-k)!}(len+k-1)\\&=\binom{len+k-1}{2k-1}\end{aligned}\)
如果 \(k=0\), 那么直接输出第三种即可 .
否则这些人把序列分成了一些区间 .
考虑计算答案的生成函数 , 那么一个区间内部的系数直接有上述的组合式给出 , 而合并区间时只需要对两个区间的生成函数卷积 , 那么分治乘法即可 .
记一下看到的一个分治乘法的新写法 , 先把所有函数弄到 deque 里 , 然后每次把队首的两个函数拿出来 , 把它们的乘积放到队尾 .
时间复杂度 \(O(n\log^2n)\)
code
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int mod=998244353;
int read()
{
int ret=0;char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c>='0'&&c<='9')ret=((ll)ret*10%mod+c-48)%mod,c=getchar();
return ret;
}
int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=(ll)a*a%mod)if(b&1)ret=(ll)ret*a%mod;return ret;}
const int maxn=2e5+5;
int n,m,k;
int a[maxn];
int R[1<<21],W[1<<21];
struct poly
{
vector<int>v;
int& operator [](int i){return v[i];}
void ntt(int L,int typ)
{
int n=pow(2,L);
for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
W[0]=1;W[1]=qpow(3,(mod-1)/n);if(typ==-1)W[1]=qpow(W[1],mod-2);
for(int i=2;i<n;i++)W[i]=(ll)W[i-1]*W[1]%mod;
if(v.size()<n)v.resize(n,0);
for(int i=0;i<n;i++)if(R[i]>i)swap(v[i],v[R[i]]);
for(int t=n>>1,d=1;d<n;d<<=1,t>>=1)
for(int i=0;i<n;i+=(d<<1))
for(int j=0;j<d;j++)
{
int tmp=(ll)W[t*j]*v[i+j+d]%mod;
v[i+j+d]=(v[i+j]-tmp+mod)%mod;
v[i+j]=(v[i+j]+tmp)%mod;
}
if(typ==-1){int inv=qpow(n,mod-2);for(int i=0;i<n;i++)v[i]=(ll)v[i]*inv%mod;}
}
poly operator *(poly &x)
{
poly ret;
int ori=v.size(),xori=x.v.size();
int L=ceil(log2(v.size()+x.v.size()-1)),n=pow(2,L);
ntt(L,1);x.ntt(L,1);
ret.v.resize(n);
for(int i=0;i<n;i++)ret[i]=(ll)v[i]*x[i]%mod;
ret.ntt(L,-1);ntt(L,-1);x.ntt(L,-1);
while(v.size()>1&&v.back()==0)v.pop_back();
while(x.v.size()>1&&x.v.back()==0)x.v.pop_back();
while(ret.v.size()>1&&ret.v.back()==0)ret.v.pop_back();
return ret;
}
};
deque<poly>q;
int fac[maxn<<1],inv[maxn<<1];
void prework()
{
fac[0]=1;for(int i=1;i<=2*n+1;i++)fac[i]=(ll)fac[i-1]*i%mod;
inv[0]=inv[1]=1;for(int i=2;i<=2*n+1;i++)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=2*n+1;i++)inv[i]=(ll)inv[i-1]*inv[i]%mod;
}
int C(int n,int m){return (ll)fac[n]*inv[n-m]%mod*inv[m]%mod;}
int main()
{
n=read();m=read();k=read();
generate_n(a+1,k,read);
prework();
if(!k){printf("%lld\n",(ll)C(n+m-1,2*m-1)*fac[m]%mod);return 0;}
if(a[1]>1)
{
poly now;now.v.resize(a[1]);
for(int i=0;i<=a[1]-1;i++)now[i]=C(a[1]-1+i,2*i);
q.push_back(now);
}
if(a[k]<n)
{
poly now;now.v.resize(n-a[k]+1);
for(int i=0;i<=n-a[k];i++)now[i]=C(n-a[k]+i,2*i);
q.push_back(now);
}
for(int i=1;i<k;i++)
{
int len=a[i+1]-a[i]-1;
poly now;now.v.resize(len+1);
for(int j=0;j<=len;j++)now[j]=C(len+j+1,2*j+1);
q.push_back(now);
}
while(q.size()>=2)
{
q.push_back(q[0]*q[1]);
q.pop_front();q.pop_front();
}
printf("%lld\n",(ll)q[0][m-k]*fac[m-k]%mod);
return 0;
}