HDU 4343 Interval query 倍增思想, DP

题目大意:

就是现在给出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
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值