rt
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 7;
struct Node{
int nor, sou; // no:north北岸的城市坐标;so:south南岸的城市坐标
}a[maxn];
bool cmp(Node p1, Node p2){
if(p1.nor != p2.nor) return p1.nor < p2.nor; // 先使得北岸升序
return p1.sou < p2.sou; // 再让南岸升序(这一条不加也可以)
}
int f[maxn]; // f[i]表示求出来的最长上升子序列中第i个位置的最优解(按照贪心的思路,肯定在某个位置的值尽可能小,留给后面的大小限制就更宽泛,所以位置i的值越小越优)
// 不难看出,f一定单调递增,具有单调性,所以能应用二分优化
void solve()
{
int n; cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i].nor >> a[i].sou;
sort(a + 1, a + n + 1, cmp);
f[1] = a[1].sou; // 第一个直接加入dp数组
int pos = 1; // pos指向f数组的末尾位置
for(int i = 2; i <= n; i ++){
if(f[pos] < a[i].sou){ // 如果它大于最后一个,说明它没有往前插的余地,直接加到最后面
f[++ pos] = a[i].sou;
}
else{ // 否则,在1~pos中找到第一个大于等于它的元素j,并将j替换为a[i].sou。这样可以给后面的元素“留下更为宽泛的限制”,更容易形成最长上升子序列。对答案更优
// lower_bound(l,r,x) 找到[l,r)区间中第一个大于等于x的元素
int idx = lower_bound(f + 1, f + pos + 1, a[i].sou) - f; // 地址相减,得到下标
f[idx] = a[i].sou; // 这里不用考虑idx>pos的情况,因为上面的if已经判断过了
}
}
cout << pos << '\n'; // pos的值即为f数组的长度,也就是LIS的长度
}
signed main()
{
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
solve();
return 0;
}