Description
给出一个长度为n的序列A(A1,A2…AN)。如果序列A不是非降的,你必须从中删去一个数,
这一操作,直到A非降为止。求有多少种不同的操作方案,答案模10^9+7。
Input
第一行一个整数n。
接下来一行n个整数,描述A。
Output
一行一个整数,描述答案。
Sample Input
4
1 7 5 3
Sample Output
18
HINT
1<=N<=2000
解题思路:
先dp出 f[i]f[i] 为长度为i的不下降序列数,可以用树状数组优化到O(n2logn)O(n2logn)
有个直观的想法就是,枚举最终序列的长度k,设长度为k的不下降子序列个数为 f[k]f[k] ,那么有 (n−k)!∗f[k](n−k)!∗f[k]种方案。
但是这样可能会有一些不合法的方案。
注意到合法方案在最后一步删之前不是不降序列,所以可以减去在最后一步删之前是一个长度为k+1k+1的不下降子序列转移来的方案数,即(n−k−1)!∗f[k+1]∗(k+1)(n−k−1)!∗f[k+1]∗(k+1)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll getint()
{
ll i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=2005,mod=1e9+7;
inline void add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
int n,m,ans,a[N],b[N],f[N],fac[N];
struct BIT
{
int a[N];
inline void insert(int i,int v){for(;i<=n;i+=i&-i)add(a[i],v);}
inline int query(int i){int res=0;for(;i;i-=i&-i)add(res,a[i]);return res;}
}bit[N];
int main()
{
//freopen("lx.in","r",stdin);
n=getint();for(int i=1;i<=n;i++)a[i]=b[i]=getint();
sort(b+1,b+n+1),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+1;
fac[0]=1;for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
bit[0].insert(1,1);
for(int i=1;i<=n;i++)
for(int j=n;j;j--)
{
int tmp=bit[j-1].query(a[i]);
add(f[j],tmp),bit[j].insert(a[i],tmp);
}
for(int i=1;i<=n;i++)add(ans,(1ll*f[i]*fac[n-i]%mod-1ll*(i+1)*f[i+1]%mod*fac[n-i-1]%mod+mod)%mod);
cout<<ans<<'\n';
return 0;
}

本文探讨了一道经典的算法题目,即如何计算将一个序列变为非降序列的不同操作方案的数量。通过动态规划与树状数组相结合的方法,文章提供了一个高效的解决方案,并给出了完整的代码实现。

被折叠的 条评论
为什么被折叠?



