题目链接:isn
上来就觉得这不是个DP就是个数学题,结果他既是DP又是数学题QAQ
设f[i][j]表示以i结尾的长度为j的下降序列,则f[i][j]的计算式为
发现暴力计算这个是的,于是我们借助树状数组求逆序对的方法计算f[i][j],这样就优化到了
最后统计答案时还要用到容斥原理。单纯考虑i贡献的答案是ans[i]=
但是可以发现我们枚举剩下(n-i)个数的删除顺序时可能已经在某个时刻形成了非降序列,按题意这时候应该停止但是我们并没有停止
在考虑我们如果枚举到了一个非法的删除序列,他一定是从i+1个数删除一个来的,我们枚举删的是哪个,减去这时候的序列个数
因为不管枚举到的这个序列合法非法,他一定被计算到了ans[i]中,所以这么做是对的
#include
#include
#include
#define LL long long
using namespace std;
const LL mod=1000000007;
const int maxn=2010;
int n,N,pos[maxn];
LL a[maxn],t[maxn],sum[maxn][maxn];
LL f[maxn][maxn],g[maxn],fac[maxn];
void init_hash(){
for (int i=1;i<=n;++i) t[i]=a[i];
sort(t+1,t+n+1);
N=unique(t+1,t+n+1)-t-1;
}
int lowbit(int x){return x&-x;}
void update(int id,int x,LL v){
for (int i=x;i<=n;i+=lowbit(i))
sum[id][i]=(sum[id][i]+v)%mod;
}
LL query(int id,int x){
LL ret=0;
for (int i=x;i;i-=lowbit(i))
ret=(ret+sum[id][i])%mod;
return ret;
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
init_hash(); fac[1]=1;
for (int i=1;i<=n;++i)
pos[i]=lower_bound(t+1,t+N+1,a[i])-t;
for (int i=2;i<=n;++i) fac[i]=1LL*fac[i-1]*i%mod;
update(0,1,1);
for (int i=1;i<=n;++i)
for (int j=i;j>=1;--j){
f[i][j]=(f[i][j]+query(j-1,pos[i])%mod)%mod;
update(j,pos[i],f[i][j]%mod);
}
for (int i=1;i<=n;++i)
for (int j=i;j<=n;++j)
g[i]=(g[i]+f[j][i])%mod;
LL ans=0;
for (int i=1;i<=n;++i)
ans=((ans%mod+(fac[n-i]%mod*g[i]%mod)%mod)%mod-fac[n-i-1]%mod*g[i+1]%mod*(i+1)%mod+mod)%mod;
printf("%lld",ans);
}