zoukankan      html  css  js  c++  java
  • [51nod1514] 美妙的序列

    Description

    如果对于一个 (1sim n) 的排列满足:

    (1sim n-1) 这些位置之后将序列断开,使得总可以从右边找一个数,使得该数不会比左边所有数都大,则称该序列是“美妙的”。

    给出 (n) ,求出长度为 (n) 的美妙的序列的数量。多组数据。 (T,nleq 10^5)。对 (998244353) 取模。

    Solution

    暴力DP是 (O(Tn^2)) 的。我们需要发现题目的隐藏性质。

    如果某个排列不是美妙的,那一定存在一个位置 (k) ,使得 (k) 右边的最小值大于左边的最大值。也就是说,(k) 左边是一个 (1sim k) 的排列。

    问题就转化成了求有多少长度为 (n) 的排列,使得任意一个前缀都不是 (1sim k) 的排列。

    考虑 (DP) 。设 (f[i]) 表示长度为 (i) 的美妙的排列个数。有转移:

    [f[n]=n!-sum_{i=1}^{n-1}i!cdot f[n-i]quad f[0]=0 ]

    这个转移的含义是用总方案数减去不合法的方案数,也就是减去所有不美妙的排列。于是枚举最靠右不能满足要求的位置,然后把序列划分成两端。前一段是一个 (1sim k) 的排列,后一段一定是一个美妙的序列(不然一定可以找到一个更靠右的不满足题意的位置)。

    这时候就可以分治FFT了。但是别着急,一般分治FFT能做的多项式求逆都可以做。再推几步式子:

    [f[n]=n!-sum_{i=1}^n i!cdot f[n-i] ]

    移项:

    [sum_{i=0}^n i!cdot f[n-i]=n! ]

    由于有 (f[0]=0) 的特殊情况所以需要特殊考虑:

    [ ext{当n=0时}quad sum_{i=0}^n i!cdot f[n-i]+1=n! ]

    于是令 (F)(f) 的生成函数,(G=sumlimits_{i=0}^infty i!cdot x^i),于是就有 (F imes G+1=G),移项可以得到:

    [F=1-frac1{G} ]

    多项式求逆即可。复杂度 (O(nlog n))

    Code

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using std::min;
    using std::max;
    using std::swap;
    using std::vector;
    typedef double db;
    typedef long long ll;
    #define pb(A) push_back(A)
    #define pii std::pair<int,int>
    #define all(A) A.begin(),A.end()
    #define mp(A,B) std::make_pair(A,B)
    #define int long long
    const int n=1e5;
    const int N=4e5+5;
    const int mod=998244353;
    
    int fac[N];
    int tmpa[N];
    int b[N],c[N];
    int lim,rev[N];
    
    int getint(){
        int X=0,w=0;char ch=getchar();
        while(!isdigit(ch))w|=ch=='-',ch=getchar();
        while( isdigit(ch))X=X*10+ch-48,ch=getchar();
        if(w) return -X;return X;
    }
    
    int ksm(int a,int b=mod-2,int ans=1){
        while(b){
            if(b&1) ans=ans*a%mod;
            a=a*a%mod;b>>=1;
        } return ans;
    }
    
    void ntt(int *f,int opt){
        for(int i=1;i<lim;i++) if(i<rev[i]) swap(f[i],f[rev[i]]);
        for(int mid=1;mid<lim;mid<<=1){
            int tmp=ksm(3,(mod-1)/(mid<<1));
            if(opt<0) tmp=ksm(tmp);
            for(int R=mid<<1,j=0;j<lim;j+=R){
                int w=1;
                for(int k=0;k<mid;k++,w=w*tmp%mod){
                    int x=f[j+k],y=w*f[j+k+mid]%mod;
                    f[j+k]=(x+y)%mod,f[j+k+mid]=(mod+x-y)%mod;
                }
            }
        } if(opt<0)
            for(int in=ksm(lim),i=0;i<lim;i++) f[i]=f[i]*in%mod;
    }
    
    void solveinv(int len,int *a,int *b){
        if(!len) return b[0]=ksm(a[0]),void();
        solveinv(len>>1,a,b);lim=len+len;
        for(int i=1;i<lim;i++) rev[i]=(rev[i>>1]>>1)|(i&1?lim>>1:0);
        for(int i=0;i<len;i++) tmpa[i]=a[i];
        ntt(tmpa,1),ntt(b,1);
        for(int i=0;i<lim;i++) b[i]=b[i]*(2ll-tmpa[i]*b[i]%mod+mod)%mod;
        ntt(b,-1);
        for(int i=len;i<lim;i++) b[i]=0;
        for(int i=0;i<lim;i++) tmpa[i]=0;
    }
    
    signed main(){
        fac[0]=1;
        for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
        solveinv(131072,fac,b);
        for(int i=1;i<=n;i++) b[i]=mod-b[i];
        int T=getint();
        while(T--){
            int x=getint();
            printf("%lld
    ",b[x]);
        } return 0;
    }
    
    
  • 相关阅读:
    自动支持图片webp格式压缩,图片服务器升级webserver
    长江商学院介绍及名人校友名单
    恒大许家印靠斗着地主,当上首富
    用GitLab搭建自己的私有GitHub
    vim快速定位到某一行的几种方法和显示行号,定位匹配字符串,显示当前行信息的命令
    mysql数据库表上百万记录优化思路杂谈
    Java打印输出数组的三种方式:传统的for循环方式,for each循环,利用Arrays类中的toString方法
    个人快速成长之系统方法,最小化成长系统
    程序员应该了解的常见软件开发定律
    用python修改文件内容修改txt内容的3种方法
  • 原文地址:https://www.cnblogs.com/YoungNeal/p/10275637.html
Copyright © 2011-2022 走看看