HDU 3530 Subsequence

84 篇文章 2 订阅
8 篇文章 0 订阅

题面

题意

多组数据
给n,k,m和n个数,问这n个数中最长满足其中最大值与最小值之差在m与k之间的子串的长度.
n<=100000

方法

因为n的范围和多组数据,必须用O(n)算法.
用单调队列来维护最大值和最小值,并据此dp

该单调队列使用时,将加入元素与队尾比较,根据维护的极值来判断比较规则,使队首表示的是该队列上一个pop的元素的位置右端到目前推入的元素的极值.
然后将两个队首作差,若大于k,则将两个队首中位置较前面的pop直至差小于等于k,此时满足条件的区间的左端点即为pop掉元素的位置的右边那个点.
再用此时的队首之差与l比较,若大于等于l,则更新答案.

注意,在操作中要时刻判断队列是否为空

代码

1.结构体

#include<bits/stdc++.h>
#define N 100010
using namespace std;

struct Dq
{
	bool dx;
	int id[N],nu[N],head,tail;
	void init()
	{
		head=1;
		tail=0;
	}
	void pop()
	{
		head++;
	}
	void push(int u,int v)
	{
		if(dx)
		{
			while(u>nu[tail]&&head<=tail) tail--;
		}
		else
		{
			while(u<nu[tail]&&head<=tail) tail--;
		}
		tail++;
		id[tail]=v;
		nu[tail]=u;
	}
	int frontn()
	{
		return nu[head];
	}
	int fronti()
	{
		return id[head];
	}
	int length()
	{
		return id[tail]-id[head]+1;
	}
	bool empty()
	{
		if(tail<head) return 1;
		else return 0;
	}
	void print()
	{
		int i;
		cout<<head<<" "<<tail<<endl;
		for(i=head;i<=tail;i++)
		{
			cout<<nu[i]<<" ";
		}
		cout<<endl;
	}
};
int n,l,r,num[N],ans;
Dq mn,mx;

int main()
{
	int i,j;
	mx.dx=1;
	mn.dx=0;
	while(~scanf("%d%d%d",&n,&l,&r))
	{
		int ll;
		ll=0;
		mx.init();
		mn.init();
		ans=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&num[i]);
			mn.push(num[i],i);
			mx.push(num[i],i);
			while(!mx.empty()&&!mn.empty()&&mx.frontn()-mn.frontn()>r)
			{
				if(mx.fronti()>=mn.fronti()) ll=max(ll,mn.fronti()),mn.pop();
				else ll=max(ll,mx.fronti()),mx.pop();
			}
			if(!mx.empty()&&!mn.empty()&&mx.frontn()-mn.frontn()>=l)
			{
				ans=max(ans,i-ll);
			}
			/*
			cout<<"mx:";
			mx.print();
			cout<<"mn:";
			mn.print();
//			for(j=1;j<=500000000;j++);
			//*/
		}
		if(r<l)
		{
			printf("0\n");
			continue;
		}
		printf("%d\n",ans);
	}
}

2.指针

#include<bits/stdc++.h>
#define N 1000005
#define max(a,b) (a>b?a:b)
using namespace std;
int a[N],ans,n,m,k,l,q1[N],q2[N],*b1,*b2,*e1,*e2;
int main()
{
    for(;~scanf("%d %d %d",&n,&m,&k);)
    {
        ans=l=0;
        e1=q1;e2=q2;b1=q1+1;b2=q2+1;
        for(int i=1;i<=n;++i)
        {
            scanf("%d",a+i);
            for(;!(b1>e1||*(a+i)<*(a+*e1));--e1);
            for(;!(b2>e2||*(a+i)>*(a+*e2));--e2);
            for(*(++e1)=*(++e2)=i;!(b1>e1||b2>e2)&&*(a+*b1)-*(a+*b2)>k;)
                *b1>*b2?l=*(b2++):l=*(b1++);
            if(!(b1>e1||b2>e2||*(a+*b1)-*(a+*b2)<m))
                ans=max(ans,i-l);
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值