题意:给定n(n<=100000)个区间(左闭右开)和m(m<=100000)次询问[l, r],问所有在[l, r]区间内最多有多少个两两不相交的区间。
题解:这个题可以暴力贪心,排序后筛选掉能覆盖其他任意区间的区间,然后扫描[l,r]得到答案。
正解:坐标离散化,将区间排序后在初始化pos[i][j](代表i坐标向右数最小的连续第2^j合法区间的右端点坐标)的过程中可以删除覆盖其他区间的区间,
然后枚举[l,r]区间内答案的二进制,最后得到答案。
PS:upper_bound:第一个大于val的指针位置。
lower_bound:第一个等于val的指针位置,如果不存在则指向第一个大于val的指针位置,此时与upper_bound的返回值相同。
upper_bound()- lower_bound 即为val在所查找数组中出现的次数,数组须为升序。
Sure原创,转载请注明出处。
暴力贪心:
#include <iostream> #include <cstdio> #include <memory.h> #include <algorithm> using namespace std; const int inf = 1000000002; const int maxn = 100002; struct interval { int l,r; bool operator < (const interval &other) const { if(l == other.l) { return r > other.r; } return l < other.l; } }inter[maxn]; int m,n,cnt; void read() { for(int i=0;i<n;i++) { scanf("%d %d",&inter[i].l,&inter[i].r); } sort(inter , inter + n); return; } void make() //筛选掉能覆盖住其他任意区间的大区间 { int tot = 0; for(int i=0;i<n;++i) { bool flag = true; for(int j=i+1;j<n;++j) { if(inter[j].l >= inter[i].l && inter[j].r <= inter[i].r) { flag = false; break; } if(inter[j].l >= inter[i].r) { break; } } if(flag) { inter[tot++] = inter[i]; } } n = tot; return; } int bis(int val) { int le = 0,ri = n-1; while(le <= ri) { int mid = (le + ri) >> 1; if(inter[mid].l >= val) { ri = mid - 1; } else { le = mid + 1; } } return le; } void dfs(int l,int r) { int wei = bis(l); //找到第一个在l右方最近区间的左端点 if(wei == n || inter[wei].l >= r || inter[wei].r > r) return; cnt++; l = inter[wei].r; return dfs(l , r); } void solve() { int fr,to; for(int i=0;i<m;i++) { scanf("%d %d",&fr,&to); cnt = 0; dfs(fr , to); printf("%d\n",cnt); } return; } int main() { while(~scanf("%d %d",&n,&m)) { read(); make(); solve(); } return 0; }
离散化+倍增思想:
#include <iostream> #include <cstdio> #include <memory.h> #include <algorithm> using namespace std; const int maxn = 100002; const int maxm = 20; struct interval { int l,r; bool operator < (const interval &other) const { if(l == other.l) { return r < other.r; } return l < other.l; } }inter[maxn]; int pos[maxn << 1][maxm],x[maxn << 1]; int m,n,tot; inline int getid(int val,bool flag) { if(flag == false) return lower_bound(x , x + tot , val) - x; else return upper_bound(x , x + tot , val) - x - 1; } void read() { tot = 0; for(int i=0;i<n;i++) { scanf("%d %d",&inter[i].l,&inter[i].r); x[tot++] = inter[i].l; x[tot++] = inter[i].r; } return; } void discretization() //坐标离散化 { sort(inter , inter + n); sort(x , x + tot); tot = unique(x , x + tot) - x; for(int i=0;i<n;i++) { inter[i].l = getid(inter[i].l , 0); inter[i].r = getid(inter[i].r , 1); } x[tot] = tot; inter[n].l = inter[n].r = tot; return; } int MIN(int a,int b) { return a < b ? a : b; } void init() { int wei = tot,ri = n-1; for(int i=tot-1;i>=0;i--) { while(ri >= 0 && inter[ri].l >= i) { wei = MIN(wei , inter[ri--].r); //可以筛掉覆盖其他区间的大区间 } pos[i][0] = wei; } for(int i=0;i<maxm;i++) { pos[tot][i] = tot; } for(int i=1;i<maxm;i++) { for(int j=0;j<tot;j++) { pos[j][i] = pos[pos[j][i-1]][i-1]; } } return; } int ask(int l,int r) { if(l > r || l == tot || r == -1) return 0; int res = 0,k = maxm; while(k) //枚举答案的二进制 { k--; if(pos[l][k] <= r) { res += (1 << k); l = pos[l][k]; } } return res; } void solve() { int fr,to; while(m--) { scanf("%d %d",&fr,&to); fr = getid(fr , 0); to = getid(to , 1); printf("%d\n",ask(fr,to)); } return; } int main() { while(~scanf("%d %d",&n,&m)) { read(); discretization(); init(); solve(); } return 0; }