●BZOJ 4361 isn

题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=4361

题解:

容斥,DP,树状数组
注意题意:一旦变成了非降序列,就停止操作。即对非降序列进行操作是非法的。
首先求出 dp[i][j]:表示以第i个数作为结尾且长度为j的不降序列的种类数
转移显然 : dp[i][j]+=dp[k][j-1] k<i且a[k]<=a[j],复杂度 O(N^3)
可以用 树状数组优化到 O(N^2*log2N),(要开 N个树状数组)
然后得到 g[j]+=dp[i][j]:表示长度为 j的不降序列的种类数。
在令 w[i]=g[i]*(N-i)!含义是留下的i个数,组成非降序列,那 N-i个数有(N-i)!种方法把它们拿走。
那么 答案就是  w[1] + w[2] + w[3] +.....
完了么?当然没有,细细一想,w[ ]好像有问题:
不是说一旦变成了非降序列,就要停止操作么,
所以 w[i]完全可能会存在某个方案还没操作完就已经非降了,那么要减去这些方案。
怎么减呢?
不难发现,w[i+1]里包含了两类方案,
一类是取了N-(i+1)个数后恰好变成非降序列,这类是合法的操作
另一类是还没有取到第N-(i+1)个数就已经非降了,那么这类操作就是非法的
同时对于 w[i]来说,其中包含的非法方案就是上面两类操作的方案数*(i+1),                  
即w[i+1]*(i+1)
所以减去就好了 : w[i]-=w[i+1]*(i+1)
最后的答案才是 w[1] + w[2] + w[3] +.....

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 2050
#define _ %mod
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
const int mod=1000000007;
struct BIT{
	int val[MAXN][MAXN],N;
	void Init(int n){
		N=n; 
		memset(val,0,sizeof(val));
	}
	int Lowbit(int x){
		return x&-x;
	}
	void Modify(int id,int p,int x){
		for(int i=p;i<=N;i+=Lowbit(i))
			val[id][i]=(1ll*val[id][i]+x)_;
	}
	int Query(int id,int p){
		int ans=0;
		for(int i=p;i;i-=Lowbit(i))
			ans=(1ll*ans+val[id][i])_;
		return ans;
	}
}t;
int dp[MAXN][MAXN],g[MAXN],a[MAXN],fac[MAXN]; //dp[i][j] 以i结尾,序列长度为 j的方案
int N,ANS;
int main()
{
	static int tmp[MAXN],M;
	scanf("%d",&N); t.Init(N); fac[0]=1;
	for(int i=1;i<=N;i++) fac[i]=(1ll*fac[i-1]*i)_;
	for(int i=1;i<=N;i++) scanf("%d",&a[i]),tmp[i]=a[i];
	sort(tmp+1,tmp+N+1); M=unique(tmp+1,tmp+N+1)-tmp-1;
	for(int i=1;i<=N;i++) a[i]=lower_bound(tmp+1,tmp+M+1,a[i])-tmp;
	for(int i=1;i<=N;i++){
		for(int j=i;j>=2;j--){ 
			dp[i][j]=t.Query(j-1,a[i]);
			t.Modify(j,a[i],dp[i][j]);
		}
		dp[i][1]=1; t.Modify(1,a[i],dp[i][1]);
	}
	for(int j=1;j<=N;j++){ 
		for(int i=1;i<=N;i++)
			g[j]=(1ll*g[j]+dp[i][j])_;
		g[j]=(1ll*g[j]*fac[N-j])_;
	} 
	for(int i=1;i<=N;i++)
		g[i]=(1ll*g[i]-(1ll*g[i+1]*(i+1))_+mod)_,ANS=(1ll*ANS+g[i])_;
	printf("%d",ANS);
	return 0;
}

posted @ 2017-12-12 19:33  *ZJ  阅读(119)  评论(0编辑  收藏  举报