qzezoj 1571 相交线
题面传送门
对于这道题,我们有一个显而易见的结论:当两条线分别为\(x1\),\(x2\),\(y1\),\(y2\)时,且\(x1>y1\&\&x2<y2\)或\(x1<y1\&\&x2>y2\)时这两条线相交
从这之中又可以推出一个定理:若我们按照左边的顺序依次选了\(k\)条线互不相交,其下标记为\(s_1,s_2.....s_k\),则\(b_{s_{i}}\)单调上升
那么我们就可以直接求最长上升子序列了
但\(O(n^2)\)的方法会\(T\),所以我们要\(O(nlog^2n)\)的方法。
我们设\(f_i\)为长度为\(i\)的单调上升子序列的末尾的最大值,那么这个\(f_i\)序列一定是单调不升的,因为如果\(f_k>f_i\&\&k>i\),那么\(k\)结尾的那个子序列肯定有上升子序列以\(k\)结尾且长度为\(i\),就不符合定义了。
我们可以利用单调性二分找到可以接上去的地方,可以更新答案的地方就更新最大值
代码实现:
#include<cstdio>
#include<cstring>
using namespace std;
int n,a,f[100039],head,ans,t;
inline void read(int &x){
char s=getchar();x=0;
while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
int find(int x) {
int l=0,r=head,mid;
while(l+1<r) {
mid=(l+r)>>1;
if(x>=f[mid]) l=mid;
else r=mid;
}
return r;
}
int main() {
scanf("%d",&t);
while(t--) {
head=0;
ans=0;
memset(f,0,sizeof(f));
read(n);
for(register int i=1; i<=n; i++) {
read(a);
if(!head||a>f[head]) {
f[++head]=a;
ans=ans>head?ans:head;
} else f[find(a)]=a;
}
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号