bzoj4361-isn

题意

给出一个数列\(a\),当这个数列不是不下降的时候我们删除其中一个数,直到它是不下降的。

问有多少种删除方案。\(n\le 2000\)

分析

直接算方案数比较难办,转而计算序列数。

如果不考虑不合法的删除(变成不下降之后继续删),那么设长度为\(i\)的不下降子序列有\(g_i\)个,那么得到长度为\(i\)的子序列的方案数为\(g_i(n-i)!\)

现在我们求出所有不管不合法的\(g_i\),那么\(g_i\)中的不合法情况必定是从\(g_{i+1}\)删除一个位置得到的,这是因为\(g_{i+1}\)包含了\(g_i\)的直接不合法情况和间接不合法情况,因为它自己包含了后面的不合法情况。例子\(a=(1,7,3,5)\)可以清楚地说明这一点。
\[ ans=\sum g_i(n-i)!-g_{i+1}(n-i-1)!(i+1) \]
\(g_i\)如何求呢?退而求其次,我们算\(f[i][j]\),表示长度为\(i\)\(j\)结尾的子序列数量。
\[ \begin{aligned} f[i][j]=\sum f[i-1][k] && k<j,a_k\le a_j \end{aligned} \]
离散化\(a\),用树状数组可以优化到\(O(n^2\log n)\)

这题关键是首先要想到求序列数,接着就是通过减掉不合法情况来计算。

代码

#include<cstdio>
#include<cctype>
#include<cstring>
#include<numeric>
#include<algorithm>
using namespace std;
typedef long long giant;
int read() {
    int x=0,f=1;
    char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=2e3+1;
const int q=1e9+7;
inline int Plus(int x,int y) {return ((giant)x+(giant)y)%q;}
inline int Multi(int x,int y) {return (giant)x*y%q;}
inline int Sub(int x,int y) {return Plus(x,q-y);}
int a[maxn],b[maxn],c[maxn],fac[maxn],n,f[maxn][maxn],g[maxn];
inline int lowbit(int x) {return x&-x;}
inline void inc(int x,int d) {for (;x<=n;x+=lowbit(x)) c[x]=Plus(c[x],d);}
inline int sum(int x) {
    int ret=0;
    for (;x;x-=lowbit(x)) ret=Plus(ret,c[x]);
    return ret;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
#endif
    n=read();
    fac[0]=1;
    for (int i=1;i<=n;++i) fac[i]=Multi(fac[i-1],i);
    for (int i=1;i<=n;++i) a[i]=b[i]=read();
    sort(b+1,b+n+1);
    int m=unique(b+1,b+n+1)-b-1;
    for (int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+m+1,a[i])-b;
    for (int i=1;i<=n;++i) f[1][i]=1;
    for (int i=2;i<=n;++i) {
        memset(c,0,sizeof c);
        for (int j=1;j<=n;++j) {
            f[i][j]=Plus(f[i][j],sum(a[j]));
            inc(a[j],f[i-1][j]);
        }
    }
    for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) g[i]=Plus(g[i],f[i][j]);
    int ans=0;
    for (int i=n;i;--i) ans=Plus(ans,Sub(Multi(g[i],fac[n-i]),Multi(Multi(g[i+1],fac[n-i-1]),i+1)));
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/owenyu/p/7151064.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值