ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[分治FFT]「CTSC2018」青蕈领主

2019-03-18 18:49:21  阅读:230  来源: 互联网

标签:ch int 青蕈 FFT 序列 CTSC2018 区间 长度 buf


题目梗概

定义一个序列是连续的,当且仅当这个序列的最大值-最小值不超过序列长度-1.

现在有一个长度为\(n\)的排列,给出以每个位置为右端点的最长连续区间的长度,求满足的排列的方案数.

解题思路

如果\(a[n]!=n\)且有区间相交显然无解.

那么我们可以根据区间的包含关系建出一棵以\(n\)为根的树,用\(s[i]\)表示节点\(i\)的儿子个数.

因为连续的区间可以看成一个点,所以每个节点的贡献可以分别考虑.

定义\(f[i]\)为长度有多少\(n+1\)的连续序列,满足在删去最大值后不存在长度大于\(1\)的连续序列

那么最后答案显然是\(\prod f[s[i]]\).

考虑\(f[i]\)的转移.

如果从合法方案转来,只要最后一个数不等于\(i\)即可,方案数为\((i-1)f[i-1]\)

如果从不合法方案转来,那么不满足的区间只有有一个,长度设为\(l\),把最大值插入形成合法区间的方案数为\(f[l]\),把插入后的区间看成一个点,与剩下的点的方案数为\(f[i-l]\),若要保证有解,那么这个区间的范围一定在\([2,i-l]\),所以得到:
\[ f[i]=(i-1)f[i-1]+\sum_{l=2}^{i-2}(i-l-1)f[i]f[i-l] \]
\(f\)用分治FFT预处理即可.

#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int tt=998244353,g=3;;
const int maxn=1400005;
char nc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF;return *l++;
}
inline int _read(){
    int num=0;char ch=nc();
    while(ch<'0'||ch>'9') ch=nc();
    while(ch>='0'&&ch<='9') num=num*10+ch-48,ch=nc();
    return num;
}
int n,T,f[maxn],A[maxn],B[maxn],re[maxn],a[maxn],top,que[maxn],s[maxn],ans,pd;
int qsm(LL w,int b){int num=1;while(b){if (b&1) num=(LL)num*w%tt;w=(LL)w*w%tt;b>>=1;}return num;}
void NTT(int a[],int f,int n){
    for (int i=0;i<n;i++) if (i<re[i]) swap(a[i],a[re[i]]);
    for (int i=1;i<n;i<<=1){
        int wn=qsm(g,(tt-1)/(i<<1)),w=1,x,y;
        if (f<0) wn=qsm(wn,tt-2);
        for (int j=0;j<n;j+=(i<<1),w=1)
        for (int k=0;k<i;k++,w=(LL)w*wn%tt){
            x=a[j+k];y=(LL)a[j+k+i]*w%tt;
            a[j+k]=(x+y)%tt;a[j+k+i]=(x-y+tt)%tt;
        }
    }
    if (f<0) for (int i=0,INV=qsm(n,tt-2);i<n;i++) a[i]=(LL)a[i]*INV%tt;
}
void work(int l,int r){
    if (l==r){if (l==2) f[l]=2;else f[l]=(f[l]+(LL)f[l-1]*(l-1)%tt)%tt;return;}
    int mid=l+(r-l>>1);work(l,mid);
    int m,len=0;for (m=1;m<=(r-l+1)*2;m<<=1) len++;
    for (int i=0;i<m;i++) re[i]=((re[i>>1]>>1)|(i&1)<<(len-1));
    for (int i=0;i<m;i++) if (i+l<=mid) A[i]=(LL)f[i+l]*(i+l-1)%tt;else A[i]=0;
    for (int i=0;i<m;i++) if (i<=r-l) B[i]=f[i];else B[i]=0;
    NTT(A,1,m);NTT(B,1,m);for (int i=0;i<m;i++) A[i]=(LL)A[i]*B[i]%tt;NTT(A,-1,m);
    for (int i=mid+1;i<=r;i++) f[i]=(f[i]+A[i-l])%tt;
    if (l!=2){
        for (int i=0;i<m;i++) if (i+l<=mid) A[i]=f[i+l];else A[i]=0;
        for (int i=0;i<m;i++) if (i<=r-l) B[i]=(LL)f[i]*(i-1)%tt;else B[i]=0;
        NTT(A,1,m);NTT(B,1,m);for (int i=0;i<m;i++) A[i]=(LL)A[i]*B[i]%tt;NTT(A,-1,m);
        for (int i=mid+1;i<=r;i++) f[i]=(f[i]+A[i-l])%tt;
    }work(mid+1,r);
}
void work(){
    for (int i=1;i<=n;i++) a[i]=_read(),s[i]=0;
    if (a[n]!=n){printf("0\n");return;}
    top=1;que[1]=n;pd=0;
    for (int i=n-1;i>=1;i--){
        while(i<que[top]-a[que[top]]+1) top--;
        if (i-a[i]<que[top]-a[que[top]]) pd=1;
        s[que[top]]++;que[++top]=i; 
    }
    if (pd){printf("0\n");return;}
    ans=1;for (int i=1;i<=n;i++) ans=(LL)ans*f[s[i]]%tt;
    printf("%d\n",ans);
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    T=_read();n=_read();work(2,n-1);f[0]=1;f[1]=2;
    while(T--) work();return 0;
} 

标签:ch,int,青蕈,FFT,序列,CTSC2018,区间,长度,buf
来源: https://www.cnblogs.com/CHNJZ/p/10554019.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有