• #前缀和优化dp#牛客练习赛71 C 数学考试


    题目

    (1sim n)的排列,有(m)个限制条件,第(i)个限制条件(p_i),
    表示前(p_i)个数不能是(1sim p_i)的排列,求符合要求的排列的个数。


    分析

    这里是单纯计数的做法,时间复杂度(O(n^2))
    (dp[i][j])表示前(i)个数均(leq j)并且必须包含(j)的方案数,
    初始化(dp[1][1sim n]=1)(如果第一个数有限制要特判),最后输出(dp[n][n])
    首先可以写出一个朴素的方程,

    [dp[i][j]=(j-i+1)dp[i-1][j]+sum_{k=i-1}^{j-1}dp[i-1][k] ]

    前面表示选完(i-1)个数已经包含(j)
    那在(1sim j)中还有(j-i+1)个数可以选择填入
    否则以前没有选择(j)且全部小于(j),那么现在选择(j)就可以了。
    对于一个限制直接让(dp[i][i]=0)就可以了
    然而这是(O(n^3))的做法,不过后面这一坨前缀和优化就可以做到(O(n^2))
    然而如果(n)很大,但是(m)还是原来的数据范围的话会直接T飞,
    但是数据还是很良心的,还是wtcl
    同步于牛客博客


    代码

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define rr register
    using namespace std;
    const int mod=20000311,N=2011;
    int n,m,a[N],dp[N][N];
    inline signed iut(){
        rr int ans=0; rr char c=getchar();
        while (!isdigit(c)) c=getchar();
        while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
        return ans;
    }
    inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    signed main(){
        n=iut(),m=iut();
        for (rr int i=1;i<=m;++i) a[i]=iut();
        for (rr int i=1;i<=n;++i) dp[1][i]=1;
        sort(a+1,a+1+m);
        for (rr int i=1,I=1;i<=n;++i){
            rr int sum=dp[i-1][i-1];
            if (i>1) for (rr int j=i;j<=n;++j)
                dp[i][j]=mo(sum,1ll*dp[i-1][j]*(j-i+1)%mod),
                sum=mo(sum,dp[i-1][j]);
            if (a[I]==i) dp[i][i]=0,++I;
        }
        return !printf("%d",dp[n][n]);
    }
    
  • 相关阅读:
    徒手用Java来写个Web服务器和框架吧<第二章:Request和Response>
    徒手用Java来写个Web服务器和框架吧<第一章:NIO篇>
    Linux使用小笔记<进程操作篇>
    shell条件判断
    rz和sz上传下载文件
    vim 快捷键
    while read读取文本内容
    云主机启动提示Booting from Hard Disk GRUB
    centos7进入单用户模式
    账号被锁无法ssh登陆
  • 原文地址:https://www.cnblogs.com/Spare-No-Effort/p/13790462.html
Copyright © 2020-2023  润新知