2021牛客暑期多校训练营9 C Cells
题意
给定n个点,分别在 ( 0 , a 1 ) , ( 0 , a 2 ) . . . ( 0 , a n ) (0,a_1),(0,a_2)...(0,a_n) (0,a1),(0,a2)...(0,an),现在这 n n n个点只能向右或向下移动,要求这 n n n个点分别走到 ( 1 , 0 ) , ( 2 , 0 ) . . . ( n , 0 ) (1,0),(2,0)...(n,0) (1,0),(2,0)...(n,0)且路径两两不相交的总方案数,注意 ( 0 , a i ) (0,a_i) (0,ai)可以走到 ( 1 , 0 ) , ( 2 , 0 ) . . . ( n , 0 ) (1,0),(2,0)...(n,0) (1,0),(2,0)...(n,0)中的任何一个。
Sol
首先不相交路径数我们优先考虑
L
G
V
LGV
LGV引理~~(当时不会,大雾)~~
根据 L G V LGV LGV,由于每个 ( 0 , a i ) (0,a_i) (0,ai)到 ( 1 , 0 ) , ( 2 , 0 ) . . . ( n , 0 ) (1,0),(2,0)...(n,0) (1,0),(2,0)...(n,0)的路径数分别为 C a i + 1 1 , C a i + 2 2 , . . . , C a i + n n C_{a_i+1}^1,C_{a_i+2}^2,...,C_{a_i+n}^n Cai+11,Cai+22,...,Cai+nn
也即 ( a i + 1 ) ! 1 ! a i ! , ( a i + 2 ) ! 2 ! a i ! , . . . , ( a i + n ) ! n ! a i ! \frac{(a_i+1)!}{1!a_i!},\frac{(a_i+2)!}{2!a_i!},...,\frac{(a_i+n)!}{n!a_i!} 1!ai!(ai+1)!,2!ai!(ai+2)!,...,n!ai!(ai+n)!
那么所对应的答案矩阵为
M
=
[
(
a
1
+
1
)
!
1
!
a
1
!
(
a
1
+
2
)
!
2
!
a
1
!
⋯
(
a
i
+
n
)
!
n
!
a
n
!
(
a
2
+
1
)
!
1
!
a
2
!
(
a
2
+
2
)
!
2
!
a
2
!
⋯
(
a
2
+
n
)
!
n
!
a
2
!
⋮
⋮
⋱
⋮
(
a
n
+
1
)
!
1
!
a
n
!
(
a
n
+
2
)
!
2
!
a
n
!
⋯
(
a
n
+
n
)
!
n
!
a
n
!
]
(5)
M= \left[ \begin{matrix} \frac{(a_1+1)!}{1!a_1!}& \frac{(a_1+2)!}{2!a_1!} & \cdots & \frac{(a_i+n)!}{n!a_n!}\\ \frac{(a_2+1)!}{1!a_2!} & \frac{(a_2+2)!}{2!a_2!} & \cdots & \frac{(a_2+n)!}{n!a_2!}\\ \vdots & \vdots & \ddots & \vdots\\ \frac{(a_n+1)!}{1!a_n!} & \frac{(a_n+2)!}{2!a_n!} & \cdots & \frac{(a_n+n)!}{n!a_n!} \end{matrix} \right] \tag{5}
M=⎣⎢⎢⎢⎢⎡1!a1!(a1+1)!1!a2!(a2+1)!⋮1!an!(an+1)!2!a1!(a1+2)!2!a2!(a2+2)!⋮2!an!(an+2)!⋯⋯⋱⋯n!an!(ai+n)!n!a2!(a2+n)!⋮n!an!(an+n)!⎦⎥⎥⎥⎥⎤(5)
然后每一列提出个
1
i
!
\frac{1}{i!}
i!1出来,再把上下约分一下
M
=
∏
i
=
1
n
1
i
!
[
(
a
1
+
1
)
(
a
1
+
1
)
(
a
1
+
2
)
⋯
(
a
1
+
1
)
(
a
1
+
2
)
.
.
.
(
a
1
+
n
)
(
a
2
+
1
)
(
a
2
+
1
)
(
a
2
+
2
)
⋯
(
a
2
+
1
)
(
a
2
+
2
)
.
.
.
(
a
2
+
n
)
⋮
⋮
⋱
⋮
(
a
n
+
1
)
(
a
n
+
1
)
(
a
n
+
2
)
⋯
(
a
n
+
1
)
(
a
n
+
2
)
.
.
.
(
a
n
+
n
)
]
(5)
M=\prod_{i = 1}^{n}\frac{1}{i!} \left[ \begin{matrix} (a_1+1)& (a_1+1)(a_1+2) & \cdots & (a_1+1)(a_1+2)...(a_1+n)\\ (a_2+1) & (a_2+1)(a_2+2) & \cdots & (a_2+1)(a_2+2)...(a_2+n)\\ \vdots & \vdots & \ddots & \vdots\\ (a_n+1) &(a_n+1)(a_n+2) & \cdots & (a_n+1)(a_n+2)...(a_n+n) \end{matrix} \right] \tag{5}
M=i=1∏ni!1⎣⎢⎢⎢⎡(a1+1)(a2+1)⋮(an+1)(a1+1)(a1+2)(a2+1)(a2+2)⋮(an+1)(an+2)⋯⋯⋱⋯(a1+1)(a1+2)...(a1+n)(a2+1)(a2+2)...(a2+n)⋮(an+1)(an+2)...(an+n)⎦⎥⎥⎥⎤(5)
以第一行为例
( a 1 + 1 ) ( a 1 + 2 ) − ( a 1 + 1 ) = ( a 1 + 1 ) 2 (a_1+1)(a_1+2)-(a_1+1)=(a_1+1)^2 (a1+1)(a1+2)−(a1+1)=(a1+1)2
( a 1 + 1 ) ( a 1 + 2 ) ( a 1 + 3 ) − 3 ( a 1 + 1 ) ( a 1 + 2 ) + ( a 1 + 1 ) = ( a 1 + 1 ) 3 (a_1+1)(a_1+2)(a_1+3)-3(a_1+1)(a_1+2)+(a_1+1)=(a_1+1)^3 (a1+1)(a1+2)(a1+3)−3(a1+1)(a1+2)+(a1+1)=(a1+1)3
以此类推:每一列都可以通过前几列华为
(
a
1
+
1
)
i
(a_1+1)^i
(a1+1)i的形式,于是
M
=
∏
i
=
1
n
1
i
!
[
(
a
1
+
1
)
(
a
1
+
1
)
2
⋯
(
a
1
+
1
)
n
(
a
2
+
1
)
(
a
2
+
1
)
2
⋯
(
a
2
+
1
)
n
⋮
⋮
⋱
⋮
(
a
n
+
1
)
(
a
n
+
1
)
2
⋯
(
a
n
+
1
)
n
]
(5)
M=\prod_{i = 1}^{n}\frac{1}{i!} \left[ \begin{matrix} (a_1+1)& (a_1+1)^2 & \cdots & (a_1+1)^n\\ (a_2+1) & (a_2+1)^2 & \cdots & (a_2+1)^n\\ \vdots & \vdots & \ddots & \vdots\\ (a_n+1) &(a_n+1)^2 & \cdots & (a_n+1)^n \end{matrix} \right] \tag{5}
M=i=1∏ni!1⎣⎢⎢⎢⎡(a1+1)(a2+1)⋮(an+1)(a1+1)2(a2+1)2⋮(an+1)2⋯⋯⋱⋯(a1+1)n(a2+1)n⋮(an+1)n⎦⎥⎥⎥⎤(5)
然后每行提一个公因式出来
M
=
∏
i
=
1
n
1
i
!
∏
i
=
1
n
(
a
i
+
1
)
[
1
(
a
1
+
1
)
⋯
(
a
1
+
1
)
n
−
1
1
(
a
2
+
1
)
⋯
(
a
2
+
1
)
n
−
1
⋮
⋮
⋱
⋮
1
(
a
n
+
1
)
⋯
(
a
n
+
1
)
n
−
1
]
(5)
M=\prod_{i = 1}^{n}\frac{1}{i!}\prod_{i = 1}^{n}(a_i+1) \left[ \begin{matrix} 1& (a_1+1) & \cdots & (a_1+1)^{n-1}\\ 1 & (a_2+1) & \cdots & (a_2+1)^{n-1}\\ \vdots & \vdots & \ddots & \vdots\\ 1 &(a_n+1) & \cdots & (a_n+1)^{n-1} \end{matrix} \right] \tag{5}
M=i=1∏ni!1i=1∏n(ai+1)⎣⎢⎢⎢⎡11⋮1(a1+1)(a2+1)⋮(an+1)⋯⋯⋱⋯(a1+1)n−1(a2+1)n−1⋮(an+1)n−1⎦⎥⎥⎥⎤(5)
到这里就很清楚了,后面的行列式是一个范德蒙行列式
M
=
∏
i
=
1
n
1
i
!
∏
i
=
1
n
(
a
i
+
1
)
∏
1
≤
i
<
j
≤
n
n
(
(
a
j
+
1
)
−
(
a
i
+
1
)
)
=
∏
i
=
1
n
1
i
!
∏
i
=
1
n
(
a
i
+
1
)
∏
1
≤
i
<
j
≤
n
n
(
a
j
−
a
i
)
M=\prod_{i = 1}^{n}\frac{1}{i!}\prod_{i = 1}^{n}(a_i+1)\prod_{1\leq i<j \leq n}^{n}((a_j+1)-(a_i+1)) \\=\prod_{i = 1}^{n}\frac{1}{i!}\prod_{i = 1}^{n}(a_i+1)\prod_{1\leq i<j \leq n}^{n}(a_j-a_i)
M=i=1∏ni!1i=1∏n(ai+1)1≤i<j≤n∏n((aj+1)−(ai+1))=i=1∏ni!1i=1∏n(ai+1)1≤i<j≤n∏n(aj−ai)
于是用
O
(
n
)
O(n)
O(n)处理处
∏
i
=
1
n
1
i
!
\prod_{i = 1}^{n}\frac{1}{i!}
∏i=1ni!1和
∏
i
=
1
n
(
a
i
+
1
)
\prod_{i = 1}^{n}(a_i+1)
∏i=1n(ai+1)
后面差的形式如果暴力则 O ( n 2 ) O(n^2) O(n2),显然不可,考虑用卷积 ( F F T / N T T ) (FFT/NTT) (FFT/NTT) ,优化到 O ( n l o g n ) O(nlogn) O(nlogn)
为什么可以这样?一般的卷积是求系数相乘,怎么才能求减法呢?
由于卷积相乘幂次是相加的,于是可以把 a i 和 − a i a_i和-a_i ai和−ai当做幂次,这样卷积完之后,遍历幂次的所有范围,如果系数不为0,说明这个幂次存在也即 ( a j − a i ) (a_j-a_i) (aj−ai)存在。
下面再考虑一个问题,卷积求的是任意两个的乘积,而我们要求的是有顺序的 1 ≤ i < j ≤ n {1\leq i<j \leq n} 1≤i<j≤n。
首先由于存在负数,于是我们把负数加上一个偏移量(
a
i
a_i
ai的最大值记为
M
M
M),于是我们幂次有
a
i
a_i
ai和
M
−
a
i
M-a_i
M−ai ,而我们最后卷积完会得到的所有幂次范围为
[
0
,
2
∗
M
]
[0,2*M]
[0,2∗M] ,这是注意题目有个条件
1
≤
i
<
j
≤
n
,
a
i
<
a
j
{1\leq i<j \leq n},a_i<a_j
1≤i<j≤n,ai<aj,于是这样卷完
(
a
j
−
a
i
)
(a_j-a_i)
(aj−ai)等价于
a
j
+
M
−
a
i
>
M
a_j+M-a_i>M
aj+M−ai>M 于是所有符合的幂次我们就从
M
+
1
M+1
M+1到
2
∗
M
2*M
2∗M枚举,其对应的系数就为这个差值出现的次数,快速幂求一下就可以了。
#include <bits/stdc++.h>
#define ll long long
#define lll __int128
using namespace std;
const int P = 998244353,G=3; // P=1004535809 ord(P)=3
const int pi=acos(-1);
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-')f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
const int N=1e6+10,M=1e6;
ll n,m;
ll rev[1<<22],bit,tot;
ll a[1<<22],b[1<<22];
ll qmi(ll x,ll y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%P;
x=x*x%P;
y>>=1;
}
return res;
}
ll inv(ll x)
{
return qmi(x,P-2);
}
void NTT(ll a[],int type)
{
for(int i=0;i<tot;i++)
if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int mid=1;mid<tot;mid<<=1)
{
ll wn=qmi(G,(P-1)/(mid*2));
if(type==-1) wn=qmi(wn,P-2);
for(int i=0,len=mid<<1;i<tot;i+=len)
{
ll w=1;
for(int j=0;j<mid;j++,w=w*wn%P)
{
ll x=a[i+j],y=w*a[i+j+mid]%P;
a[i+j]=(x+y)%P,a[i+j+mid]=(x-y+P)%P;
}
}
}
if(type==-1)
{
ll inv_=inv(tot);
for(int i=0;i<tot;i++)
a[i]=a[i]*inv_%P;
}
}
int main()
{
n=read();
ll ans=1;
for(int i=1;i<=n;i++)
{
ll x;
x=read();
ans=ans*(x+1)%P;
a[x]=1,b[M-x]=1;
}
ll k1=1,k2=1;
for(int i=1;i<=n;i++)
{
k1=k1*i%P;
k2=k2*k1%P;
}
ans=ans*inv(k2)%P;
//cout<<ans<<endl;
tot=1<<21,bit=21;
for(int i=0;i<tot;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
NTT(a,1),NTT(b,1);
for(int i=0;i<tot;i++) a[i]=a[i]*b[i]%P;
NTT(a,-1);
for(int i=1e6+1;i<=M*2;i++)
{
if(a[i])
{
ans=ans*qmi(i-1e6,a[i])%P; //求的是 (a_j - a_i ) (0<i<j<=n&&a_i<a_j)
//所以枚举>M的数就是(a_j-a_i)
}
}
cout<<ans<<endl;
puts("");
return 0;
}