题目大意:
就是现在给出N个区间(纠结了一下应该都是开区间), 区间(l, r)满足两边端点都是不超过10^9的非负整数, 现在有M次询问, 每次询问区间(L, R)中最多可以找出多少个区间(来自于N), 这些区间互不相交且都是(L, R)的子区间
N, M <= 10^5
大致思路:
第一次写这种题......被vector的lower_bound和upper_bound弄得纠结了一会儿= =
首先要将所有的区间离散化处理, 将输入的区间的左右端点的值排序后去重离散化到2*10^5以内
然后对于每一次询问, 也离散化一下...然后这里被lower_bound()卡了一下, lower_bound(begin, end, val)在val存在时返回的是它的第一次出现的位置, upper_bound(begin, end, val)在val存在时返回的是val最后一次出现的位置+1, 但是当val不存在时, upper_bound(begin, end, val)和lower_bound(begin, end, val)返回的都是如果这个数存在, 那么它应该存在于哪个位置
处理好离散化之后, 对所有的区间按照左端点为第一关键字, 右端点第二关键字排序, 两个关键字都是递增
以贪心的思想, 如果将能覆盖其他区间的区间去掉, 则不会影响答案, 然后就是在去掉这些区间之后, 排序好的区间将会满足左端点坐标递增, 同时右端点坐标也递增(如果不递增就被覆盖了), 那么考虑倍增算法的思想, 用dp[i][j]表示当前横坐标在i位置, 从这个位置向右走共找到2^j个满足条件的区间之后, 右端点的位置, 当不能找到这么多区间的时候这个的值就是右界, 那么有递推方程dp[i][j] = dp[ dp[i][j - 1] ] [j - 1], 而算出dp[i][0]只需要从右向左对排好序的区间进行一遍扫描就可以得到了, 在扫描的同时还可以去重
代码如下:
Result : Accepted Memory : 17912 KB Time : 1404 ms
/*
* Author: Gatevin
* Created Time: 2015/8/3 12:56:59
* File Name: Sakura_Chiyo.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define maxn 100010
#define maxm 18
struct Interval
{
int l, r;
Interval(int _l, int _r) : l(_l), r(_r) {}
Interval(){}
};
bool cmp(Interval i1, Interval i2)
{
return i1.l < i2.l || (i1.r < i2.r && i1.l == i2.l);
}
int N, M;
Interval in[maxn];
vector<int> V;
int dp[2*maxn][18];//用dp[i][j]表示从横坐标为i开始, 向右走(1 << j)个有效区间的区间的右端点坐标
int main()
{
while(~scanf("%d %d", &N, &M))
{
V.clear();
for(int i = 0; i < N; i++)
{
scanf("%d %d", &in[i].l, &in[i].r);
V.push_back(in[i].l);
V.push_back(in[i].r);
}
sort(V.begin(), V.end());
int size = unique(V.begin(), V.end()) - V.begin();
V.resize(size);
for(int i = 0; i < N; i++)
{
in[i].l = lower_bound(V.begin(), V.end(), in[i].l) - V.begin();
in[i].r = lower_bound(V.begin(), V.end(), in[i].r) - V.begin();//这里in[i].r一定存在所以没有问题
}//离散化完成
sort(in, in + N, cmp);//按左端点第一关键字递增, 右端点为第二关键字递增排序
//先处理出所有的dp[i][0]
int R = size, pos = N - 1;
for(int i = size - 1; i >= 0; i--)//横坐标
{
while(pos >= 0 && in[pos].l >= i)
R = min(R, in[pos--].r);
dp[i][0] = R;
}
for(int j = 1; j < maxm; j++)
for(int i = 0; i < size; i++)
if(dp[i][j - 1] < size)
dp[i][j] = dp[dp[i][j - 1]][j - 1];
else dp[i][j] = size;
int l, r;
while(M--)
{
scanf("%d %d", &l, &r);
l = lower_bound(V.begin(), V.end(), l) - V.begin();
//r = lower_bound(V.begin(), V.end(), r) - V.begin(); lower_bound在元素不存在时返回假设这个元素存在时应该在的位置
r = upper_bound(V.begin(), V.end(), r) - V.begin() - 1;
if(l == size || r <= l || r < 0)
{
puts("0");
continue;
}
int k = maxm;
int ans = 0;
while(k)
{
k--;
if(dp[l][k] <= r)
{
ans += (1 << k);
l = dp[l][k];
}
}
printf("%d\n", ans);
}
}
return 0;
}
/*
1 55
1 5
1 4
*/