题目
n
n
n个人从
(
0
,
a
i
)
(0,a_i)
(0,ai)走到
(
i
,
0
)
(i,0)
(i,0),只能往右或往下走。
求每个人路径两两不相交的方案数。
n
≤
1
e
5
n\leq 1e5
n≤1e5
正解
有个东西叫
L
G
V
LGV
LGV引理……
假设有两个人,分别从
(
0
,
a
1
)
(0,a_1)
(0,a1)到
(
1
,
0
)
(1,0)
(1,0)、从
(
0
,
a
2
)
(0,a_2)
(0,a2)到
(
2
,
0
)
(2,0)
(2,0)。
考虑用总数减去不合法,不合法是什么呢,相当于将两个人的终点交换。
推广一下,多个人怎么搞。考虑容斥,容斥系数可以盲猜到是逆序对数……
然后就可以发现答案是个行列式。设矩阵为
M
M
M。
M
i
,
j
=
(
a
i
+
j
j
)
M_{i,j}=\binom{a_i+j}{j}
Mi,j=(jai+j)
将
1
j
!
\frac{1}{j!}
j!1提出来,于是要求
M
i
,
j
=
(
a
i
+
1
)
j
‾
M_{i,j}=(a_i+1)^{\overline j}
Mi,j=(ai+1)j
这个上升幂可以看成关于
a
i
+
1
a_i+1
ai+1的多项式。在矩阵中消一波得到
M
i
,
j
=
(
a
i
+
1
)
j
M_{i,j}=(a_i+1)^j
Mi,j=(ai+1)j,行列式不变。
接下来就是个范德蒙德行列式:形如
M
i
,
j
=
x
i
j
−
1
M_{i,j}=x_i^{j-1}
Mi,j=xij−1,求其行列式。
有个结论:
det
(
M
)
=
∏
i
<
j
(
x
j
−
x
i
)
\det (M)=\prod_{i<j}(x_j-x_i)
det(M)=∏i<j(xj−xi)
归纳可证。百度百科上讲得很清楚。
求这个东西可以直接卷积求出每个因子的出现次数。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 500010
#define ll long long
#define mo 998244353
#define mo2 998244353ll*998244353ll
#define M (1<<21)
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int n;
ll fac[N],ifac[N];
void initC(int n){
fac[0]=1;
for (int i=1;i<=n;++i)
fac[i]=fac[i-1]*i%mo;
ifac[n]=qpow(fac[n]);
for (int i=n-1;i>=0;--i)
ifac[i]=ifac[i+1]*(i+1)%mo;
}
int a[N],mx;
int nN,re[M];
void setlen(int n){
int bit=0;
for (nN=1;nN<=n;nN<<=1,++bit);
for (int i=1;i<nN;++i)
re[i]=re[i>>1]>>1|(i&1)<<bit-1;
}
void dft(int A[],int flag){
for (int i=0;i<nN;++i)
if (i<re[i])
swap(A[i],A[re[i]]);
static ll wnk[M];
for (int i=1;i<nN;i<<=1){
ll wn=qpow(3,flag==1?(mo+1)/(2*i):mo-1-(mo+1)/(2*i));
wnk[0]=1;
for (int k=1;k<i;++k)
wnk[k]=wnk[k-1]*wn%mo;
for (int j=0;j<nN;j+=i<<1)
for (int k=0;k<i;++k){
ll x=A[j+k],y=A[j+k+i]*wnk[k];
A[j+k]=(x+y)%mo;
A[j+k+i]=(x-y+mo2)%mo;
}
}
if (flag==-1){
ll invn=qpow(nN);
for (int i=0;i<nN;++i)
A[i]=(ll)A[i]*invn%mo;
}
}
void multi(int c[],int a[],int b[],int n){
setlen(n*2);
dft(a,1);
dft(b,1);
for (int i=0;i<nN;++i)
c[i]=(ll)a[i]*b[i]%mo;
dft(c,-1);
}
int p[M],q[M],f[M];
int main(){
// freopen("in.txt","r",stdin);
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%d",&n);
initC(n);
ll ans=1;
for (int i=1;i<=n;++i)
scanf("%d",&a[i]),(ans*=a[i]+1)%=mo,mx=max(mx,a[i]);
for (int j=1;j<=n;++j)
(ans*=ifac[j])%=mo;
for (int i=1;i<=n;++i){
p[a[i]]++;
q[mx-a[i]]++;
}
multi(f,p,q,mx);
for (int i=1;i<=mx;++i)
(ans*=qpow(i,f[mx+i]))%=mo;
printf("%lld\n",ans);
return 0;
}