1. 程式人生 > >【BZOJ】5342: [Ctsc2018]青蕈領主 -分治FFT&單調棧

【BZOJ】5342: [Ctsc2018]青蕈領主 -分治FFT&單調棧

傳送門:bzoj5342


題解

因為是每個右端點的極大連續區間,所以區間不能相交,只能是包含關係。

L n n L_n\neq n 或者存在區間相交,則無解。

若有解,則每個點對應的極大連續區間向它右邊第一個完全包含它的極大連續區間連邊,必然構成了一顆樹。

對於某個結點,它自身代表一段連續區間,可以把每個兒子結點代表的一段連續區間看做一個點,記它的兒子結點個數為 s z [ i ] sz[i]

,這就是一個長度為 s z [ i ] + 1 sz[i]+1
的排列。

f n f_n 表示 1 n + 1 1-n+1 的滿足只有一個長度大於1的極大連續區間且長度為 n + 1 n+1 的排列個數。

那麼總答案為 i = 1 n f s z [ i ] \prod \limits_{i=1}^n f_{sz[i]}

問題在於求解 f f ,遞推式如下:

f n = ( n 1 ) f n 1 + l = 2 n 2 ( n l 1 ) f l f n l f_n=(n-1)f_{n-1}+\sum \limits_{l=2}^{n-2}(n-l-1)f_lf_{n-l}

每次只讓最後一個位置有極大連續區間不好遞推列舉,所以考慮置換一下原序列 a i a_i ,設 b a i = i b_{a_i}=i ,則置換後的序列 b b 中的值 i j i-j 的一段下標依次為 a i a j a_i-a_j ,所以 b b 中連續的一段區間就對應著 a a 中連續的一段區間。那麼只讓最後一個位置有極大連續區間轉化成了只讓序列中最大的一個數有極大連續區間。

轉移時分類討論:

  • 從合法序列轉移而來:假設原序列為 2 n + 1 2-n+1 的一個排列,插入一個 1 1 就得到了一個新的合法序列,注意不能將 1 1 插在 2 2 旁邊。方案數: ( n + 1 2 ) f n 1 (n+1-2)f_{n-1}
  • 從不合法序列轉移而來:此時序列中最多隻能有一個不經過最大值的極大連續區間(設長度為 l l ),插入一個數之後合法等價於將一個序列分成左右兩個都不存在極大連續區間的部分,方案數: f l f_{l} 。而將這段插入數後得到區間看做一個點,和其它 n l n-l 個數排列後的方案數為 f n l f_{n-l}
    而這段區間的值域為 [ x , x + l 1 ] [x,x+l-1] ,不經過最大值: x + l 1 < n x+l-1<n -> x n l x\leq n-l ,為保證序列只存在經過最大值的極大連續區間,所以將序列整體 + 1 +1 後插入 1 1 ,故 x 2 x\geq2 。總共有 n l 1 n-l-1 個合法值域。總方案數: ( n l 1 ) f l f n 1 (n-l-1)f_lf_{n-1} ( 2 l n 2 ) (2\leq l\leq n-2)

分治 F F T FFT 預處理出 f f (注意常數),單調棧求出 s z i sz_i 即可。


程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10,M=50010,mod=998244353,gen=3;

int T,n,len,ivg,f[M],sz[M],val[M],ans;
int rv[N],a[N],b[N],stk[M],top;

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}

char cp,OS[100];
inline void rd(int &x)
{
    cp=getchar();x=0;
    for(;!isdigit(cp);cp=getchar());
    for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

inline void ot(int x)
{
    int re=0;
    for(;(!re)||(x);x/=10) OS[++re]='0'+x%10;
    for(;re;--re) putchar(OS[re]);
    putchar('\n');
}

inline int fp(int x,int y)
{
    int re=1;
    for(;y;y>>=1,x=(ll)x*x%mod)
      if(y&1) re=(ll)re*x%mod;
    return re;
}

inline void ntt(int *e,int pr)
{
    int i,j,k,pd,ori,ix,iy,G=pr?gen:ivg;
    for(i=1;i<len;++i) if(i<rv[i]) swap(e[i],e[rv[i]]);
    for(i=1;i<len;i<<=1){
        ori=fp(G,(mod-1)/(i<<1));
        for(j=0;j<len;j+=(i<<1)){
            pd=1;
            for(k=0;k<i;++k,pd=(ll)pd*ori%mod){
                ix=e[j+k];iy=(ll)pd*e[i+j+k]%mod;
                e[j+k]=ad(ix,iy);e[i+j+k]=dc(ix,iy);
            }
        }
    }
    if(pr) return;
    G=fp(len,mod-2);
    for(i=0;i<len;++i) e[i]=(ll)e[i]*G%mod;
}

inline void init(int n)
{
    int i,L=0;
    for(len=1;len<n;len<<=1) L++;
    for(i=1;i<len;++i) rv[i]=((rv[i>>1]>>1)|((i&1)<<(L-1)));
}

inline void mul(int *f,int *g)
{
    ntt(f,1);ntt(g,1);
    for(int i=0;i<len;++i) f[i]=(ll)f[i]*g[i]%mod;
    ntt(f,0);
}

void cdq(int l,int r)
{
    if(l==r) {
       if(l==2) f[l]=2;
       else f[l]=ad(f[l],(ll)(l-1)*f[l-1]%mod);
       return;
    }
    
    int i,mid=(l+r)>>1;
    cdq(l,mid);
    init((r-l)<<1);
    for(i=l;i<=mid;++i) a[i-l]=(ll)f[i]*(i-1)%mod;
    for(i=mid+1-l;i<len;++i) a[i]=0;
    for(i=l;i<=r;++i) b[i-l]=f[i-l];
    for(i=r-l+1;i<len;++i) b[i]=0;
    
    mul(a,b);
    for(i=mid+1;i<=r;++i) f[i]=ad(f[i],a[i-l]);

    if(l!=2 && r>l+1){
    	for(i=l;i<=mid;++i) a[i-l]=f[i];
        for(i=mid+1-l;i<len;++i) a[i]=0;
        for(i=l;i<=r;++i) b[i-l]=(ll)f[i-l]*(i-l-1)%mod;
        for(i=r-l+1;i<len;++i) b[i]=0;
        
        mul(a,b);
        for(i=mid+1;i<=r;++i) f[i]=ad(f[i],a[i-l]);
    }
    cdq(mid+1,r);
}

int main(){
    int i,x,pr;
    rd(T);rd(n);ivg=fp(gen,mod-2);
    if(n>2) cdq(2,n-1);f[0]=1;f[1]=2;
    for(;T;--T){
        for(i=1;i<=n;++i) rd(val[i]),val[i]=i-val[i]+1;
        if(val[n]!=1){puts("0");continue;}
        top=pr=0;
        for(i=1;i<=n;++i){
            sz[i]=0;x=val[i];
            for(;top && val[stk[top]]>=x;--top) sz[i]++;
            if(stk[top]!=x-1) {pr=1;break;}
            stk[++top]=i;
        }
        if(pr) {puts("0");continue;}
        for(ans=i=1;i<=n;++i) 
          ans=(ll)ans*f[sz[i]]%mod;
        ot(ans);
    }
    return 0;
}