把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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;
}
posted @ 2020-03-18 15:36  275307894a  阅读(35)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end