hdu 4604 Deque

总体思想就是二分的LIS+标记重复的数

从后往前遍历数组:                         

每运行一个数的时候,可以找出从这个数到数组尾的最长不上升子序列的长度num和最长不下降子序列的长度nums。                

然后用sum标记这个数在最长不上升子序列中存在的个数,用sums标记这个数在最长不下降子序列中存在的个数。                

然后如果 deque数组中第一个放入的数是这个数的话, deque的长度就等于num+nums-min(sum,sums);

遍历一边之后,找出最大的数就可以了。





#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
struct list
{
	int i;
	int x;
	int y;
}node[200001];
int num[200005];
int len[200005];
int sum[200005];
int lens[200005];
int sums[200005];
int cmp1(list a,list b)
{
	return a.x<b.x;
}
int cmp2(list a,list b)
{
	return a.i<b.i;
}
int main()
{
	int T,n,i;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(i=0;i<n;i++)
		{
			scanf("%d",&node[i].x);
			node[i].i=i;
		}
		sort(node,node+n,cmp1);
		int leap=2;
		node[0].y=1;
		for(i=1;i<n;i++)
		{
			if(node[i].x==node[i-1].x)node[i].y=node[i-1].y;
			else node[i].y=leap++;
		}
		sort(node,node+n,cmp2);
		for(i=0;i<n;i++)num[i]=node[n-i-1].y;
		int l,r;
		l=r=0;
		len[r++]=num[0];
		memset(sum,0,sizeof(sum));
		sum[num[0]]++;
		int ls,rs;
		ls=rs=0;
		lens[rs++]=num[0];
		memset(sums,0,sizeof(sums));
		sums[num[0]]++;
		int maxnn;
		maxnn=1;
		for(i=1;i<n;i++)
		{
			int l1,l2,mid;
			if(num[i]>=len[r-1])
			{
				len[r++]=num[i];
				sum[num[i]]++;
				l1=r;
			}
			else
			{
			    l=0;
			    int rr=r;
				while(l<r)
				{
					mid=(l+r)/2;
					if(len[mid]<=num[i])l=mid+1;
					else if(len[mid]>num[i]&&(mid==0||len[mid-1]<=num[i]))break;
					else r=mid;
				}
				sum[len[mid]]--;
				sum[num[i]]++;
				len[mid]=num[i];
				l1=mid+1;
				r=rr;
			}
			if(num[i]<=lens[rs-1])
			{
				lens[rs++]=num[i];
				sums[num[i]]++;
				l2=rs;
			}
			else
			{
			    ls=0;
			    int rr=rs;
				while(ls<rs)
				{
					mid=(ls+rs)/2;
					if(lens[mid]>=num[i])ls=mid+1;
					else if(lens[mid]<num[i]&&(mid==0||lens[mid-1]>=num[i]))break;
					else rs=mid;
				}
				sums[lens[mid]]--;
				sums[num[i]]++;
				lens[mid]=num[i];
				l2=mid+1;
				rs=rr;
			}
			if(maxnn<(l1+l2-min(sum[num[i]],sums[num[i]])))maxnn=l1+l2-min(sum[num[i]],sums[num[i]]);
		}
		cout<<maxnn<<endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值