BZOJ4361 isn
题目链接:戳我
我们先处理出来长度为i的子序列分别都有多少个,记\(g[i]\)表示长度为i的不降子序列个数。
\(a[i]\)表示第i个数的数值。设\(f[i][j]\)表示以a[i]这个数结尾,且序列长度为j的子序列个数。
那么就有DP式为\(f[i][j]=\sum f[k][j-1](a[k]>=a[i])\)
对于一个确定的不降子序列,如果它的长度为i的话,那么不考虑排除不合法的,贡献数为\(g[i]*(n-i)!\)。现在考虑什么情况不合法呢?显然是有可能在序列长度为i+1的时候就已经不降了qwq,我们没有办法再删,但还是删掉了。所以要减去\(g[i+1]*(i+1)*(n-i-1)!\)
为什么减去不合法情况的时候只需要考虑\(i+1\)长度,却不考虑那些\(i+2,i+3\)的呢?因为如果那些更长的删掉一个数之后肯定还是合法的,而它们(比如说长度为i+2)删掉之后的情况个数都已经在删掉这个数之后的长度的情况中(i+1长度)计算到了。如果减去(i+1长度的),再减去(i+2长度的),就相当于多减去了qwqwq$$
代码如下:
\(O(n^3)\)做法:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 2010
#define mod 1000000007
using namespace std;
int n,ans;
int f[MAXN][MAXN],g[MAXN],dp[MAXN],a[MAXN],c[MAXN];
inline void init()
{
c[0]=1;
for(int i=1;i<=n;i++) c[i]=1ll*c[i-1]*i%mod;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
freopen("ce.out","w",stdout);
#endif
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) f[i][1]=1;
for(int i=1;i<=n;i++)
for(int j=2;j<=i;j++)
for(int k=1;k<i;k++)
{
if(a[k]>a[i]) continue;
f[i][j]=(f[i][j]+f[k][j-1])%mod;
}
for(int j=1;j<=n;j++)
for(int i=1;i<=n;i++)
g[j]=(g[j]+f[i][j])%mod;
init();
for(int i=1;i<=n;i++)
ans=(ans+1ll*g[i]*c[n-i]%mod-1ll*g[i+1]*(i+1)%mod*c[n-i-1]%mod+mod)%mod;
printf("%d\n",ans);
return 0;
}
树状数组优化后\(O(n^2logn)\)做法:
记得要先对数值离散化qwqwq
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 2010
#define mod 1000000007
using namespace std;
int n,ans;
int f[MAXN][MAXN],g[MAXN],dp[MAXN],a[MAXN],c[MAXN],tree[MAXN],pre[MAXN];
inline void init()
{
c[0]=1;
for(int i=1;i<=n;i++) c[i]=1ll*c[i-1]*i%mod;
}
inline void add(int x,int k)
{
for(int i=x;i<=n;i+=i&(-i))
tree[i]=(tree[i]+k)%mod;
}
inline int query(int x)
{
int cur_ans=0;
for(int i=x;i;i-=i&(-i))
cur_ans=(cur_ans+tree[i])%mod;
return cur_ans%mod;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
freopen("ce.out","w",stdout);
#endif
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
memcpy(pre,a,sizeof(a));
pre[n+1]=0;
sort(&pre[1],&pre[2+n]);
int tot=unique(&pre[1],&pre[2+n])-pre-1;
for(int i=0;i<=n;i++) a[i]=lower_bound(&pre[1],&pre[1+tot],a[i])-pre;
for(int i=1;i<=n;i++) f[i][1]=1;
for(int j=2;j<=n;j++)
{
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;i++)
{
add(a[i-1],f[i-1][j-1]);
f[i][j]=query(a[i])%mod;
}
}
for(int j=1;j<=n;j++)
for(int i=1;i<=n;i++)
g[j]=(g[j]+f[i][j])%mod;
init();
for(int i=1;i<=n;i++)
ans=(ans+1ll*g[i]*c[n-i]%mod-1ll*g[i+1]*(i+1)%mod*c[n-i-1]%mod+mod)%mod;
printf("%d\n",ans);
return 0;
}