Day2 T4 区间

题目

  Alice收到一些很特别的生日礼物:区间。即使很无聊,Alice还是能想出关于区间的很多游戏,其中一个是,Alice从中选出最长的不同区间的序列,其中满足每个区间必须在礼物中,序列中每个区间必须包含下一个区间。编程计算最长序列的长度。

输入

第一行包含一个整数N(1 (leqslant) N (leqslant) 100000),表示区间的个数。
接下来N行,每行两个整数A和B描述一个区间(1 (leqslant) A (leqslant) B (leqslant) 1000000)。

输出

输出满足条件的序列的最大长度。

样例

输入 输出
3
3 4
2 5
1 6
3
5
10 30
20 40
30 50
10 60
30 40
3
6
1 4
1 5
1 6
1 7
2 5
3 5
5

题解

  先按照l从小到大排序,l相同时根据r从大到小排序,这样我们就只需要求出r的最长不上升子序列。那么问题就来了:如何求最长不上升子序列?如果直接求,将会发现直接使用lower_bound和upper_bound都是错的;要手写二分需要注意边界,否则红红火火。
  怎么办?
  有一个小技巧,我们可以先取负,那么求原序列的最长不上升子序列就相当于求新序列的最长不下降子序列;Addition:upper_bound和lower_bound自带有一个神奇的操作:greater<int>,可以让原来的>和(geqslant)变成<和(leqslant),也可以用来求最长不上升子序列。

#include<bits/stdc++.h>
using namespace std;
int n;
int f[100010],t[100010];

struct node{
	int l,r;
};
node p[100010];

bool cmp(node x,node y){
	if(x.l!=y.l) return x.l<y.l;
	else return x.r>y.r;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d %d",&p[i].l,&p[i].r);
	sort(p+1,p+n+1,cmp);
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=n;i++) t[i]=-p[i].r;
	f[1]=t[1];
	int len=1;
	for(int i=2;i<=n;i++){
		if(t[i]>=f[len]) f[++len]=t[i];
		else{
			int temp=upper_bound(f+1,f+len+1,t[i])-f;
			f[temp]=min(t[i],f[temp]);
		}
	}
	printf("%d",len);
	return 0;
}

PS:我曾想当然的认为,l相同时根据r从小到大排序,然后直接求r的最长不下降子序列就可以更方便地解决这个问题,但是事实证明这样做是错的。
[1,9]
[1,18]
[1,23]
[2,6]
[2,9]
[2,13]
这组数据可以hack掉此做法。
用此做法,排序后r依次为
9 18 23 6 9 13
最长不下降子序列为3;
用正确做法排序后r依次为
-23 -18 -9 -13 -9 -6
最长不下降子序列为5。

原文地址:https://www.cnblogs.com/znk161223/p/11453105.html