bzoj4361 isn(樹狀數組優化DP)

要點:一個不合法的狀態一定是由一個長度多1的不下降子序列轉移來的,直接減掉即可。

然後就轉化爲求長度爲i的不下降子序列個數。定義f[i,j]表示以i結尾的,長度爲j的不下降子序列個數,則f[i,j] = sum(f[k,j-1]), k<i&&a[k]<=a[i]。直接轉移是n三方的,可以離散化後用樹狀數組維護前綴和。注意由於i這一維在樹狀數組中被省去了,枚舉j需要倒着枚舉防止被自己更新。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define LL long long
using namespace std;
const int inf = 0x3f3f3f3f, MAXN = 2005;
const int mo = 1000000007;

inline void add(int&a, const int&b) { a+=b; if(a>=mo) a-=mo; }
int N, a[MAXN], dat[MAXN], dn;
int jc[MAXN] = {1};
int f[MAXN][MAXN];

int bt[MAXN][MAXN]; // 長度i,值j
int idx(int x) { return lower_bound(dat+1, dat+dn+1, x) - dat; }
int h[MAXN];

void add(int k, int i, int x)
{
	for (; i<=dn; i+=i&-i)
		add(bt[k][i], x);
}
int qsum(int k, int i)
{
	int r = 0;
	for (; i>0; i-=i&-i)
		add(r, bt[k][i]);
	return r;
}

int main()
{
	scanf("%d", &N);
	rep(i, 1, N) scanf("%d", a+i), dat[i] = a[i];
	sort(dat+1, dat+N+1);
	dn = unique(dat+1, dat+N+1) - dat-1;
	rep(i, 1, N) a[i] = idx(a[i]);
	rep(i, 1, N) jc[i] = 1ll * i * jc[i-1] % mo;
	add(0, 1, 1); //0會使樹狀數組死循環
	rep(i, 1, N) erp(j, i, 1) //j倒着循環防止被自己更新
	{
		f[i][j] = qsum(j-1, a[i]);
		add(h[j], f[i][j]);
		add(j, a[i], f[i][j]);
	}
	int ans = 0;
	rep(i, 1, N)
	{
		add(ans, 1ll * jc[N-i] * h[i] % mo);
		if (i != N) add(ans, mo - 1ll * jc[N-i-1] * (i+1) % mo * h[i+1] % mo);
	}
	printf("%d\n", ans);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章