zoukankan      html  css  js  c++  java
  • BZOJ 4361: isn

    传送门

    见计数想容斥

    考虑先求出 $F[i]$ 表示每种长度的不下降子序列的方案数,但是可能有多算,因为这样有算 从长度 $i+1$ 的不下降子序列变成长度为 $i$ 的不下降子序列的情况

    而根据题目的要求一旦序列不下降就要停止操作,但是可以发现 $F[i]$ 只要扣掉 $F[i+1]*(i+1)$ 就行了

    现在考虑一下怎么求 $F[i]$,看到 $n<=2000$,考虑 $dp$,设 $f[i][j]$ 表示以第 $i$ 个数为结尾,长度为 $j$ 的不降子序列方案数

    那么枚举前 $i$ 个数所有小于第 $i$ 个数的值 $A[i]$ 的位置 $k$,$f[i][j]+=f[k][j-1]$

    这个转移显然可以优化,把数离散化后,对每个长度 $j$ 开一个权值数状数组维护

    求出 $f$ 以后 $F[i]=(sum_{j=1}^{n}f[j][i]) cdot (n-i)!$(乘上 $(n-i)!$ 是因为那 $n-i$ 个删掉的数可以按任意顺序删除)

    最后 $Ans=sum_{i=1}^{n}F[i]$

    具体看代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2007,mo=1e9+7;
    int n,B[N],m,fac[N],Ans;
    struct dat{
        int v,id;
        inline bool operator < (const dat &tmp) const {
            return v<tmp.v;
        }
    }A[N];
    int f[N][N],g[N],ans[N];
    int t[N][N];
    inline int fk(int x) { return x>=mo ? x-mo : x; }
    inline void add(int p,int x,int y) { while(x<=m) t[p][x]=fk(t[p][x]+y),x+=x&-x; }
    inline int query(int p,int x) { int res=0; while(x) res=fk(res+t[p][x]),x-=x&-x; return res; }
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++) A[i].v=read(),A[i].id=i;
        sort(A+1,A+n+1);
        for(int i=1;i<=n;i++)
        {
            if(i==1||A[i].v!=A[i-1].v) m++;
            B[A[i].id]=m;//离散化
        }
        fac[0]=1; for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mo;
        for(int i=1;i<=n;i++)
        {
            f[i][1]=1;
            for(int j=2;j<=n;j++) f[i][j]=query(j-1,B[i]);
            for(int j=1;j<=n;j++) add(j,B[i],f[i][j]);
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) g[j]=fk(g[j]+f[i][j]);
        for(int i=n;i>=1;i--)
        {
            Ans=fk(Ans+ 1ll*g[i]*fac[n-i]%mo );
            if(i!=n) Ans=fk(Ans+mo - 1ll*g[i+1]*fac[n-i-1]%mo*(i+1)%mo );
        }
        printf("%d",Ans);
        return 0;
    }
  • 相关阅读:
    简单工厂模式实例
    浅析面向对象和面向过程
    equals与“==”的区别
    IIS挂起网站配置文件地址
    先安装win7时IIS的安装
    验证码的使用
    c#引用命名空间的作用
    ADO与ADO.NET的区别
    常用的数据库访问方式
    Exercise 11: Asking Questions
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10785094.html
Copyright © 2011-2022 走看看