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;
}