• HDU4343Interval query 倍增


    去博客园看该题解

    题意

      给定n个区间[a,b),都是左闭右开,有m次询问,每次询问你最多可以从n个区间中选出多少[L,R]的子区间,使得他们互不相交。 n,m<=10^5。 区间下标<=10^9。

    题解

      这题要用倍增

      首先,给区间按照左端点编号排个序。

      如果区间A包含了区间B,那么A一定没用,扔了。

      那么剩余的区间[x,y]的x和y一定都是升序的。

      之后,就是对于区间的贪心了:

      找到一个区间[xi,yi]之后,一定是寻找一个xj>yi且xj最小的那个区间[xj,yj],所以设该区间的编号j=next[i];这个只要二分查找一下就可以了。

      那么贪心的时候就是不断的走next,这样就出现了一个O(nm)的算法。

      那么倍增怎么做呢?

      设nxt[i][j]为第i个区间next 2^j 次后的区间编号,那么:

      nxt[i][0]=next[i],nxt[i][j]=nxt[nxt[i][j-1]][j-1

      于是就可以做了。

      不过这里我要提醒一点:C++的变量名如果用了"next",在HDU是无法通过编译的,我因此贡献了8次CE……

    代码

    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    using namespace std;
    const int N=100000+5;
    int n,m;
    int Next[N][20];
    bool alive[N];
    struct Seg{
        int x,y;
        bool operator < (const Seg &a) const{
            if (x==a.x)
                return y>a.y;
            return x<a.x;
        }
    }a[N];
    void Thrown(){
        int n_=0,miny=1e9+1;
        for (int i=n;i>=1;i--)
            if (a[i].y>=miny)
                alive[i]=0;
            else
                alive[i]=1,miny=min(miny,a[i].y);
        for (int i=1;i<=n;i++)
            if (alive[i])
                a[++n_]=a[i];
        n=n_;
    }
    int findx(int x){
        int le=1,ri=n,mid,ans=n+1;
        while (le<=ri){
            mid=(le+ri)>>1;
            if (a[mid].x==x)
                return mid;
            if (a[mid].x>x)
                ri=mid-1,ans=mid;
            else
                le=mid+1;
        }
        return ans;
    }
    int solve(int L,int R){
        int st=findx(L),ans=1,k;
        if (a[st].y>R)
            return 0;
        for (k=0;(1<<k)<=n-st;k++);
        for (;k>=0;k--)
            if ((1<<k)<=n-st&&a[Next[st][k]].y<=R)
                st=Next[st][k],ans+=1<<k;
        return ans;
    }
    int main(){
        while (~scanf("%d%d",&n,&m)){
            memset(Next,0,sizeof Next);
            for (int i=1;i<=n;i++)
                scanf("%d%d",&a[i].x,&a[i].y),a[i].y--; 
            sort(a+1,a+n+1);
            Thrown();
            a[n+1].y=1e9+1;
            a[0].y=1e9+1;
            for (int i=n;i>=1;i--){
                Next[i][0]=findx(a[i].y+1);
                for (int j=1;(1<<j)<=n-i;j++)
                    Next[i][j]=Next[Next[i][j-1]][j-1];
            }
            for (int i=1,L,R;i<=m;i++){
                scanf("%d%d",&L,&R);
                printf("%d
    ",solve(L,R));
            }
        }
        return 0;
    }
  • 相关阅读:
    [php]php时间戳当中关于时区的问题
    [jQuery] jQuery如何获取同一个类标签的所有的值
    sed 命令基础
    Docker 学习第6课
    Docker 学习第五课
    Docker 学习第四课
    Docker 学习第三课
    Docker 学习第二课
    Docker学习第一课
    XdeBug的使用
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/HDU4343.html
Copyright © 2020-2023  润新知