HDU4521 – 小明系列问题——小明序列(LIS+线段树)

题目链接:HDU4521

【分析】看到了LIS+线段树才会写的,还是太弱;这个其实就是LIS稍微变化了一下,DP方程变成了dp[i] = max{dp[j]}+1 (0 <= j < i-d-1 && a[j] < a[i]);直接这样做复杂度是O(n^2)的,数据量为10^5,必然超时。看到max{dp[j]}就想到了可以用线段树来优化,用线段树存max{dp[j]},线段树的区间用a[]的值表示,这样询问[ 0,a[i]-1 ]就保证了a[j] < a[i],但是由于需要隔开d个位置,所以加入dp[i]的时候更新dp[i+d+1]就保证了[i,i+d]这段还没被加入线段树,这样最后答案就是max{dp[]}。

【AC CODE】421ms

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <map>
//#include <unordered_map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
#define id(x,y) ((x)+(y)|(x)!=(y))
const int INF = 0x3f3f3f3f, MAXN = 100000+10;
int mx[MAXN<<1];//线段树维护值(a[])的区间最大的序列长度(dp[])

inline void push_up(int x, int y, int m){
	mx[id(x,y)] = max(mx[id(x,m)],mx[id(m+1,y)]);
}
int p,v;
void update(int x, int y)
{
	if(x == y)
	{
		mx[id(x,y)] = max(mx[id(x,y)],v);
		return;
	}
	int m = (x+y)>>1;
	if(p <= m) update(x,m);
	else update(m+1,y);
	push_up(x,y,m);
}
int ql,qr;
int query(int x, int y)
{
	if(ql <= x && y <= qr) return mx[id(x,y)];
	int m = (x+y)>>1, ans = 0;
	if(ql <= m) ans = max(ans,query(x,m));
	if(qr > m) ans = max(ans,query(m+1,y));
	return ans;
}
int dp[MAXN], a[MAXN];

int main()
{
#ifdef SHY
	freopen("e:\\1.txt", "r", stdin);
#endif
	int n,d;
	while(~scanf("%d %d*c", &n, &d))
	{
		int ma = 0;
		rep(i,0,n) scanf("%d%*c", &a[i]), ma = max(ma,a[i]);
		if(d >= n)
		{
			puts("1");
			continue;
		}
		clc(mx,0);
		repe(i,0,d) dp[i] = 1;
		int ans = 1;
		rep(i,0,n-d-1)
		{
			p = a[i];v = dp[i];
			update(0,ma);
			if(!a[i+d+1])
			{
				dp[i+d+1] = 1;
				continue;
			}
			ql = 0, qr = a[i+d+1]-1;
			dp[i+d+1] = query(0,ma)+1;
			ans = max(ans,dp[i+d+1]);
		}
		printf("%d\n", ans);
	}
	return 0;
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注

请输入正确的验证码