【BZOJ】5342: [Ctsc2018]青蕈领主 -分治FFT&单调栈

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/84321359

传送门: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;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/84321359