【BZOJ4361】isn 动态规划+树状数组+容斥

【BZOJ4361】isn

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

题解:想到动归+树状数组+容斥,但是容斥系数想复杂了~

我们希望先求出所有长度为i的非降序列个数,可以用DP解决。设f[i][j]表示最大值为i,长度为j的非降序列个数,用树状数组优化转移即可。

然后用g[i]表示$\sum f[..][j]$。因为其它数删除的顺序可以随便选,所以g[i]*=(n-i)!。但是有可能删到一半就已经得到了一个非降序列,怎么除去不合法状态呢?容斥呗!

一开始想了半天容斥系数,但其实g[i]-=g[i+1]*(i+1)即可。因为删到一般就得到非降序列情况刚好是g[i+1]。

时间复杂度$O(n^2\log n)$

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int P=1000000007;
int n,m;
ll ans;
int val[2010],v[2010],p[2010];
int s[2010][2010];
ll f[2010],c[2010][2010],jc[2010],jcc[2010],ine[2010];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
	return ret*f;
}
bool cmp(const int &a,const int &b)
{
	return val[a]<val[b];
}
inline void updata(int x,int y,int z)
{
	if(!z)	return ;
	f[y]+=z;
	for(int i=x;i<=m;i+=i&-i)
	{
		s[y][i]+=z;
		if(s[y][i]>=P)	s[y][i]-=P;
	}
}
inline int query(int x,int y)
{
	int i,ret=0;
	for(i=x;i;i-=i&-i)
	{
		ret+=s[y][i];
		if(ret>=P)	ret-=P;
	}
	return ret;
}
int main()
{
	n=rd();
	int i,j;
	jc[0]=jc[1]=jcc[0]=jcc[1]=ine[0]=ine[1]=1;
	for(i=2;i<=n;i++)	jc[i]=jc[i-1]*i%P,ine[i]=P-(P/i)*ine[P%i]%P,jcc[i]=jcc[i-1]*ine[i]%P;
	for(i=0;i<=n;i++)
	{
		c[i][0]=1;
		for(j=1;j<=i;j++)	c[i][j]=(c[i-1][j-1]+c[i-1][j])%P;
	}
	for(i=1;i<=n;i++)	val[i]=rd(),p[i]=i;
	sort(p+1,p+n+1,cmp);
	for(i=1;i<=n;i++)
	{
		if(i==1||val[p[i]]>val[p[i-1]])	m++;
		v[p[i]]=m;
	}
	for(i=1;i<=n;i++)
	{
		for(j=i;j>=2;j--)	updata(v[i],j,query(v[i],j-1));
		updata(v[i],1,1);
	}
	for(i=1;i<=n;i++)	f[i]=f[i]%P*jc[n-i]%P;
	for(i=1;i<=n;i++)	f[i]=(f[i]-f[i+1]*(i+1)%P+P)%P,ans=(ans+f[i])%P;
	printf("%lld",ans);
	return 0;
}//4 1 2 3 4

 

posted @ 2017-12-09 11:30  CQzhangyu  阅读(564)  评论(0编辑  收藏  举报