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\n",solve(L,R));
        }
    }
    return 0;
}

 

posted @ 2017-07-30 14:01  zzd233  阅读(437)  评论(0编辑  收藏  举报