• JZOJ6776【NOI2020模拟7.31】T2 path(LGV引理+范德蒙德行列式)


    Description

    • (n)个人,第(i)个人一开始在((0,a_i)),最后要走到((i,0)),每次只能从((i,j))走到((i+1,j))或者((i,j-1))
    • 求使得路径两两不相交的方案数,对(998244353)取模。经过同一点算相交。
    • (a_i≤a_{i+1},n≤5*10^5,0≤a_i<10^6)

    Solution

    • 假设只有两个人,我们交换他们的终点,他们的路径必然相交;可以发现交换终点后的方案包含了所有相交方案。
    • (n>2)时,发现这就是一个容斥的问题。设排列(sigma)的方案数为(omega(sigma)),其逆序对数为(N(sigma)),答案应该是(sum_{sigma} (-1)^{N(sigma)}omega(sigma))
    • 实际上有一个叫LGV引理的东西。这篇专栏里有一点介绍:https://zhuanlan.zhihu.com/p/64482425。
    • 然后OI Wiki上的例题就是本题的弱化版……

    • 根据LGV引理,我们要求(A_{i,j}=C_{a_i+j}^j)的行列式。每一列将(frac 1{j!})提出来,就变成((a_i+1)^overline j)。随便归纳可知,我们可以通过初等变换把它消成((a_i+1)^j)
    • 范德蒙德行列式:一个方阵(A)中的每一列都是以(x_i)为公比的等比数列,则(det A=prod_{i>j}(x_i-x_j))。这个也可以随便归纳证明。
    • 本题最后的式子就变成了:
    • 后面的那个东西要卷积。具体地说,我们把(sum x^{a_i})(sum x^{-a_i})卷起来,结果中([x^k])的意义即是差为(k)的次数,然后用个快速幂乘起来。由于这是指数,正常来说应该对(998244352)取模;但如果起点相同我们可以直接判掉,这样(a_i)就两两不同,一个差的出现次数最多只有(n/2)
    • 时间复杂度(O(nlog n))

    Code

    #include<cstdio>
    #include<algorithm>
    #define P(x,y) x=(x+y)%mo
    #define T(x,y) x=x*(y)%mo
    #define fo(i,a,b) for(int i=a;i<=b;i++)
    #define go(i,a,b) for(int i=a;i< b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    using namespace std;
    typedef long long ll;
    const ll mo=998244353;
    ll fpow(ll x,ll y) {ll a=1; for(;y;y/=2,T(x,x))if(y&1)T(a,x); return a;}
    namespace NTT
    {
    	const int NM=1<<22;
    	int tf[NM]; ll w[NM];
    	void build(int n)
    	{
    		for(int i=1;i<n;i*=2)
    		{
    			w[i]=1; ll v=fpow(3,(mo-1)/2/i);
    			go(j,1,i) w[i+j]=w[i+j-1]*v%mo;
    		}
    	}
    	void DFT(ll a[NM],int n,bool f)
    	{
    		go(i,1,n)
    		{
    			tf[i]=tf[i/2]/2+(i&1)*(n/2);
    			if(i<tf[i]) swap(a[i],a[tf[i]]);
    		}
    		ll v;
    		for(int i=1;i<n;i*=2) for(int j=0;j<n;j+=2*i) go(k,0,i)
    			v=a[i+j+k]*w[i+k]%mo, a[i+j+k]=(a[j+k]-v+mo)%mo, P(a[j+k],v);
    		if(f)
    		{
    			reverse(a+1,a+n);
    			v=fpow(n,mo-2);
    			go(i,0,n) T(a[i],v);
    		}
    	}
    	void mtp(ll a[NM],ll b[NM],int n1)
    	{
    		DFT(a,n1,0); DFT(b,n1,0);
    		go(i,0,n1) T(a[i],b[i]);
    		DFT(a,n1,1);
    	}
    }
    using namespace NTT;
    const int N=1<<22,M=1e6;
    int n;
    ll x,la,fac[N]={1},a[N],b[N],ans=1;
    int main()
    {
    	freopen("path.in","r",stdin);
    	freopen("path.out","w",stdout);
    	scanf("%d",&n);
    	fo(i,1,n)
    	{
    		scanf("%lld",&x), a[x]++, b[M-x]++, T(ans,x+1), fac[i]=fac[i-1]*i%mo;
    		if(i>1&&x==la) {puts("0"); return 0;}
    		la=x;
    	}
    	x=fpow(fac[n],mo-2);
    	fd(i,n,1) T(ans,x), T(x,i);
    	build(N/2);
    	mtp(a,b,N/2);
    	fo(i,M+1,M*2) if(a[i]) T(ans,fpow((i-M+mo)%mo,a[i]));
    	printf("%lld",ans);
    }
    
  • 相关阅读:
    蓝桥杯基础练习 杨辉三角形
    蓝桥杯基础练习 回文数 特殊的数字
    普及图论三题 P1807 P1113 P 4017
    P3916 图的遍历
    [转载][总结]图论入门:建图,DFS,BFS,拓扑排序
    如何转载博客园的文章
    P1892 [BOI2003]团伙
    P1621 集合
    [模板]线性筛素数(欧拉筛)
    P5076 普通二叉树(简化版)
  • 原文地址:https://www.cnblogs.com/Iking123/p/13410868.html
Copyright © 2020-2023  润新知