P10592 BZOJ4361 isn
當一個序列刪成非降序列的話那操作就要停止,所以我們要求的是最後一步剛好刪成非降序列的運算元,但是這樣做太複雜了,我們先不考慮停止操作,讓他一直刪下去。
這時我們就要知道長度為 \(i\) 的非降序列的數量然後才能計算答案,我們有 \(f_{i,j}\) 為第 \(i\) 個數長度為 \(j\) 的非降序列的長度,我們可以用樹狀陣列最佳化,比如我們要求以 \(3\) 結尾的長度為 \(2\) 的非降子序列的數量,我們就用樹狀陣列查詢小於 \(3\) 的長度 \(1\) 的非降子序列的數量,這樣計算的時間複雜度為 \(O(n^2\log n)\)。
接下我我們要統計長度為 \(i\) 的非降序列的總數量,我們有:
\[g_i=\sum_{i=1}^{n}\sum_{j=i}^{n}f_{j,i}
\]
現在知道數量如何計算方案數,對於刪成一個長度為 \(i\) 的非降序列的方案數為 \((n-i)!\),即刪除數的全排列。總的方案數為:
\[ans=\sum_{i=1}^n g_i\times (n-i)!
\]
接下來加上停止操作,我們要排除不合法的方案,當我們刪除最後一個數使得非降序列長度為 \(i\),我們是從 \(i+1\) 轉移過來,但是如果 \(i+1\) 刪掉最後一個數使得非降序列長度為 \(i+1\) 的話就會停止操作使得不會再向下傳遞使我們造成貢獻,所以我們要容斥掉那一部分,總計算為:
\[ans=\sum_{i=1}^n g_i\times (n-i)!-g_{i+1}\times (n-i-1)!\times (i+1)
\]
乘 \(i+1\) 是因為我們有 \(i+1\) 個數可以作長度為 \(i\) 的非降序列的最後一個刪的數。
#include <bits/stdc++.h>
#define int long long
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define re register
#define pir pair<int,int>
const int inf=1e9;
const int N=2e5+10;
const int mod=1e9+7;
using namespace std;
int n;
int a[N];
int b[N];
int f[3005][3005];
int t[3005][3005];
int fac[2005];
int g[N];
int lb(int x){
return x&-x;
}
void change(int x,int k,int z){
x++;
while(x<=2000){
(t[x][z]+=k)%=mod;
x+=lb(x);
}
}
int query(int x,int k){
x++;
int ans=0;
while(x){
ans+=t[x][k];
x-=lb(x);
ans%=mod;
}
return ans;
}
void init(){
fac[0]=1;
for(int i=1;i<=2000;i++){
fac[i]=fac[i-1]*i;
fac[i]%=mod;
}
sort(b+1,b+n+1);
int len=unique(b+1,b+n+1)-b-1;
for(re int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+len+1,a[i])-b;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(re int i=1;i<=n;i++){
cin>>a[i];
b[i]=a[i];
}
init();
change(0,1,0);
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=i;j;j--){
f[i][j]=query(a[i],j-1);
change(a[i],f[i][j],j);
}
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
(g[i]+=f[j][i])%=mod;
}
}
for(int i=1;i<=n;i++){
int x=g[i]*fac[n-i]%mod;
int y=g[i+1]*fac[n-i-1]%mod*(i+1)%mod;
if(i==n){
y=0;
}
ans=(ans+x-y+mod)%mod;
}
cout<<ans;
return 0;
}