【HDU 4343】Interval query(倍增)

BUPT2017 wintertraining(15) #8D

题意

给你x轴上的N个线段,M次查询,每次问你[l,r]区间里最多有多少个不相交的线段。(0<N, M<=100000)
限时15000 MS

题解

如果不看限时,当作是1000MS的话= =,那么可以用倍增来做。
先按右端点排序。
可以去掉一下包含了其它区间的区间,可以优化一点点。
用 f[i][j] 表示 i 节点下 \(2^n\) 个不相交的线段下标。
预处理出 f 数组。
查询的时候,左端点用二分,然后右端点用倍增来找。
实际上不用倍增,也可以卡时间过。

代码

780ms

#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 100005
using namespace std;
struct Seg{
    int s,e;
    bool operator < (const Seg &b) const{
    	return e<b.e||e==b.e&&s>b.s;
    }
}seg[N];
int n,m;
int f[N][32];
int main(){
	while(~scanf("%d %d", &n, &m)){
		for(int i=0;i<n;++i)
			scanf("%d %d", &seg[i].s, &seg[i].e);
		memset(f,-1,sizeof f);
		sort(seg,seg+n);
		int nn=0;
		for(int i=1;i<n;++i)
			if(seg[nn].s<seg[i].s)
				seg[++nn]=seg[i];
		++nn;
		for(int i=0;i<nn;++i){
			f[i][0]=i+1;
			while(f[i][0]<nn&&seg[f[i][0]].s<seg[i].e)++f[i][0];
			if(f[i][0]==nn)f[i][0]=-1;
		}
		for(int j=0;j<30;++j)
		for(int i=0;i<nn;++i)
			if(f[i][j]!=-1)
				f[i][j+1]=f[f[i][j]][j];
		for(int i=1,s,e;i<=m;++i){
			scanf("%d %d", &s, &e);
			int l=0, r=nn-1;
			while(l<r){
				int m=l+r>>1;
				if(seg[m].s<s)
					l=m+1;
				else
					r=m;
			}
			if(seg[l].e>e||seg[l].s<s)puts("0");
			else{
				int ans=1;
				for(int j=29;j>=0;--j){
					if(f[r][j]!=-1&&seg[f[r][j]].e<=e){
						r=f[r][j];
						ans+=(1<<j);
					}
				}
				printf("%d\n", ans);
			}
		}
	}
	return 0;
}

1045ms

#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 100005
using namespace std;
struct Seg {
    int s, e;
    bool operator < (const Seg &b) const {
        return e < b.e || e == b.e && s > b.s;
    }
} seg[N];
int n, m;
int main() {
    while(~scanf("%d %d", &n, &m)) {
        for(int i = 0; i < n; ++i)
            scanf("%d %d", &seg[i].s, &seg[i].e);
        sort(seg, seg + n);
        int nn = 0;
        for(int i = 1; i < n; ++i)
            if(seg[nn].s < seg[i].s)
                seg[++nn] = seg[i];
        ++nn;
        for(int i = 1, s, e; i <= m; ++i) {
            scanf("%d %d", &s, &e);
            int l = 0, r = nn - 1;
            while(l < r) {
                int m = l + r >> 1;
                if(seg[m].s < s)
                    l = m + 1;
                else
                    r = m;
            }
            if(seg[l].e > e || seg[l].s < s)puts("0");
            else {
                int ans = 1;
                for(int i = l, j = l + 1; j < nn && seg[j].e <= e; ++j) {
                    if(seg[i].e <= seg[j].s) {
                        i = j;
                        ++ans;
                    }
                }
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}
posted @ 2017-07-07 23:15  水郁  阅读(422)  评论(2编辑  收藏  举报
……