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;
}
posted @ 2019-02-25 08:38  风浔凌  阅读(125)  评论(0编辑  收藏  举报