2021牛客暑期多校训练营9 C Cells

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=1ni!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=1ni!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=1ni!1i=1n(ai+1)111(a1+1)(a2+1)(an+1)(a1+1)n1(a2+1)n1(an+1)n1(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=1ni!1i=1n(ai+1)1i<jnn((aj+1)(ai+1))=i=1ni!1i=1n(ai+1)1i<jnn(ajai)
于是用 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 aiai当做幂次,这样卷积完之后,遍历幂次的所有范围,如果系数不为0,说明这个幂次存在也即 ( a j − a i ) (a_j-a_i) (ajai)存在。

下面再考虑一个问题,卷积求的是任意两个的乘积,而我们要求的是有顺序的 1 ≤ i < j ≤ n {1\leq i<j \leq n} 1i<jn

首先由于存在负数,于是我们把负数加上一个偏移量( a i a_i ai的最大值记为 M M M),于是我们幂次有 a i a_i ai
M − a i M-a_i Mai ,而我们最后卷积完会得到的所有幂次范围为 [ 0 , 2 ∗ M ] [0,2*M] [0,2M] ,这是注意题目有个条件 1 ≤ i < j ≤ n , a i < a j {1\leq i<j \leq n},a_i<a_j 1i<jn,ai<aj​,于是这样卷完 ( a j − a i ) (a_j-a_i) (ajai)等价于 a j + M − a i > M a_j+M-a_i>M aj+Mai>M 于是所有符合的幂次我们就从 M + 1 M+1 M+1 2 ∗ M 2*M 2M枚举,其对应的系数就为这个差值出现的次数,快速幂求一下就可以了。

#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;

    
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值