HDU4343[Interval query]--倍增思想+二分+离散

博主吐槽一下:本蒟蒻差点就屎在这道鬼畜题上了QAQ(大佬可以忽视)

【链接】
HDU4343

【题目大意】
给你N个区间,和M个询问,对于每个询问,输出询问的一段区间内最多有多少段互不相交的区间。

【解题报告】
对于这题我们首先有一个笨蛋的想法,就是先按照每段区间的左端点为第一关键词,右端点为第二关键词升序排序。对于每次询问区间,O(N)的贪心,从小到大枚举,如果能选就选,如果两端区间相交挑右端点小的,时间复杂度是O(N*M)。因为选出的几段区间的编号肯定是排好序的所有区间编号的序列中的一段子序列,所以我们可以想到优化,记nxt[i]表示如果选i,选的下一段区间的编号,nxt[i]可以用二分来确定。但是nxt[i]一个一个跳还是太慢了,好像还是过不去。那么,又该如何优化呢?似乎可以用倍增思想,设nxt[i][j]表示当前在i点,跳2^j步到i+2^j编号的点(Ps:因为这里是点直接开数组是开不下的,需要离散),转移方程是nxt[i][j]=nxt[nxt[i][j-1]][j-1],时间复杂度O(M*log(N))。至此,这题就完美解决了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100005,maxm=18,maxv=200005,INF=((1<<30)-1)*2+1;
int n,m,Size,nxt[maxv][maxm],in[maxv];
struct wjd
{
    int x,y;
    bool operator < (const wjd &a) const{
        return x<a.x||(x==a.x&&y<a.y);
    }
}a[maxn];
inline int Read()
{
    int res=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=getchar();
    return res;
}
int Get(int L,int R)
{
    L=lower_bound(in+1,in+Size,L)-in;
    R=upper_bound(in+1,in+Size,R)-in-1;
    if (L>R||L>=Size) return 0;
    int sum=0;
    for (int i=maxm-1; i>=0; i--)
     if (nxt[L][i]<=R) sum+=1<<i,L=nxt[L][i];
    return sum;
}
void Work()
{
    for (int i=1; i<=n; i++) a[i]=(wjd){Read(),Read()},in[i*2-1]=a[i].x,in[i*2]=a[i].y;
    sort(in+1,in+2*n+1);
    Size=unique(in+1,in+2*n+1)-in;//去重。
    for (int i=1; i<=n; i++) a[i]=(wjd){lower_bound(in+1,in+Size,a[i].x)-in,lower_bound(in+1,in+Size,a[i].y)-in};//重新构造a数组,即离散。
    sort(a+1,a+1+n);
    for (int i=Size-1,now=n,R=Size; i; i--)
    {
        while (now&&a[now].x>=i) R=min(R,a[now].y),now--;
        nxt[i][0]=R;
    }
    for (int j=1; j<maxm; j++)
     for (int i=1; i<Size; i++)
      if (nxt[i][j-1]<Size) nxt[i][j]=nxt[nxt[i][j-1]][j-1]; else nxt[i][j]=Size;//构造nxt数组。
    for (int i=1; i<=m; i++)
    {
        int L=Read(),R=Read();
        printf("%d\n",Get(L,R));
    }
}
int main()
{
    freopen("4343.in","r",stdin);
    freopen("4343.out","w",stdout);
    while (scanf("%d%d",&n,&m)==2) Work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值