6775. 2020.07.31【NOI2020】模拟T2

题目

n n n个人从 ( 0 , a i ) (0,a_i) (0,ai)走到 ( i , 0 ) (i,0) (i,0),只能往右或往下走。
求每个人路径两两不相交的方案数。
n ≤ 1 e 5 n\leq 1e5 n1e5


正解

有个东西叫 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=xij1,求其行列式。
有个结论: det ⁡ ( M ) = ∏ i < j ( x j − x i ) \det (M)=\prod_{i<j}(x_j-x_i) det(M)=i<j(xjxi)
归纳可证。百度百科上讲得很清楚。
求这个东西可以直接卷积求出每个因子的出现次数。


代码

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值