博主吐槽一下:本蒟蒻差点就屎在这道鬼畜题上了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;
}