【LGV不相交路径】JZOJ6775.【NOI2020.07.31模拟】path

30 篇文章 0 订阅
8 篇文章 0 订阅

Description

  • 网格图中,有 n n n个起点,第 i i i个点为 ( 0 , a i ) (0,a_i) (0,ai),保证 a i ≤ a i + 1 a_i\le a_{i+1} aiai+1,求一种路径方案,使得 i i i号点到 ( i , 0 ) (i,0) (i,0),且路径没有共用点的方案数,规定从 ( x , y ) (x,y) (x,y)只能到 ( x + 1 , y ) (x+1,y) (x+1,y) ( x , y − 1 ) (x,y-1) (x,y1)
  • 模998244353, n ≤ 1 e 6 n\le1e6 n1e6

Solution

  • 这题是 L G V LGV LGV的裸题。
  • 首先考虑只有两个点的时候,那么方案数就应该是 A A A A ′ A' A B B B B ′ B' B方案数减去 A A A B ′ B' B B B B A ′ A' A方案数,因为这样子一定相交。
  • 考虑扩展,那么这个容斥我们不妨考虑 A , B , C A,B,C A,B,C A ′ , B ′ , C ′ A',B',C' A,B,C的对应关系,如果交换 A ′ , B ′ A',B' A,B的对应就会乘上 − 1 -1 1的容斥系数,同理再交换也会有 − 1 -1 1的容斥系数,那么考虑最后的对应关系的排列 p p p,应该就是 ( − 1 ) p 逆 序 对 个 数 (-1)^{p逆序对个数} (1)p
  • 这个实际上就是一个求行列式的过程。
  • 问题转化为求 A i , j = C a i + j j A_{i,j}=C_{a_i+j}^j Ai,j=Cai+jj的行列式。将它写成下降幂的形式,即为 1 j ! ( a i + 1 ) j ˉ \frac{1}{j!}(a_i+1)^{\bar{j}} j!1(ai+1)jˉ,考虑 ( a i + 1 ) j ˉ (a_i+1)^{\bar{j}} (ai+1)jˉ是一个关于 a i a_i ai j j j次多项式,那么对于同一行 a i a_i ai相同,从左往右可以消元。
  • 具体来说, ∏ k = 0 j − 1 ( a i + 1 + k ) \prod_{k=0}^{j-1}(a_i+1+k) k=0j1(ai+1+k),直接二项式展开即可得到关于 ( a i + 1 ) m (a_i+1)^m (ai+1)m的许多项,由于前面得到了 ( a i + 1 ) m (a_i+1)^m (ai+1)m,减一减就可以得到 A i , j = ( a i + 1 ) j A_{i,j}=(a_i+1)^j Ai,j=(ai+1)j
  • 然后套用范德蒙德行列式即可得到:
    在这里插入图片描述
  • 后面的NTT优化一下,计算每一个 k = ( a j + 1 ) − ( a i + 1 ) k=(a_j+1)-(a_i+1) k=(aj+1)(ai+1)的出现次数,最后乘上 k c n t k^{cnt} kcnt即可。由于单调性这个出现次数是 O ( n ) O(n) O(n)的,所以不需要考虑对 998244352 998244352 998244352取模,直接对 998244353 998244353 998244353取模即可。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define mo 998244353
#define maxn 2100000
#define ll long long 
using namespace std;

int n,i,j,k,a[maxn];
ll fct[maxn],ans;

ll ksm(ll x,ll y){
	ll s=1;
	for(;y;y/=2,x=x*x%mo) if (y&1)
		s=s*x%mo;
	return s;
}

void read(int &x){
	x=0; char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}

int bt[maxn],lim,mx; ll A[maxn],B[maxn],C[maxn];
void dft(ll *a,int sig){
	for(int i=0;i<lim;i++) if (i<bt[i]) swap(a[i],a[bt[i]]);
	for(int mid=1;mid<lim;mid<<=1){
		ll gn=ksm(3,(mo-1)/(mid<<1));
		if (sig<0) gn=ksm(gn,mo-2);
		for(int j=0;j<lim;j+=mid<<1){
			ll g=1;
			for(int k=0;k<mid;k++,g=g*gn%mo){
				ll x=a[j+k],y=a[j+k+mid]*g;
				a[j+k]=(x+y)%mo;
				a[j+k+mid]=(x-y)%mo;
			}
		}
	}
	if (sig<0){
		ll inv=ksm(lim,mo-2);
		for(int i=0;i<lim;i++) a[i]=a[i]*inv%mo;
	}
}

int main(){
	freopen("ceshi.in","r",stdin);
//	freopen("path.in","r",stdin);
//	freopen("path.out","w",stdout);
	read(n),ans=1;
	for(i=1;i<=n;i++) read(a[i]),a[i]++,ans=ans*a[i]%mo,mx=max(mx,a[i]);
	fct[0]=1;for(i=1;i<=n;i++) fct[i]=fct[i-1]*i%mo;
	fct[n]=ksm(fct[n],mo-2);
	for(i=n-1;i>=1;i--) fct[i]=fct[i+1]*(i+1)%mo;
	for(i=1;i<=n;i++) ans=ans*fct[i]%mo;
	for(lim=1;lim<=mx*2;lim<<=1);
	for(i=1;i<lim;i++) bt[i]=(bt[i>>1]>>1)|((i&1)*(lim>>1));
	for(i=1;i<=n;i++) A[a[i]]++,B[mx-a[i]]++;
	dft(A,1),dft(B,1);
	for(i=0;i<lim;i++) A[i]=A[i]*B[i]%mo;
	dft(A,-1);
	for(i=mx+1;i<=mx+mx;i++) if (A[i])
		ans=ans*ksm(i-mx,(A[i]+mo)%mo)%mo;
	printf("%lld",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值