题目传送门
好题
解法:
因为区间互不包含。
那么左端点在区间内最右边的的区间一定是当前区间能扩展到最远的区间。
这句话有点绕。举个例子。
假设当前区间为1~5。
那么我们往右扩展。
找到左端点最右且小于等于5的区间。
这个区间一定是最优解。
因为区间互不包含啊。如果这个区间不是最优解的话那么就被别的包含了。。
因为题目是环。需要转化成链。
所以把环复制一份放在后面就变成了链了。
如果当前区间起点为l。整个环长度为mx。
那么我们需要覆盖l到l+mx
然后我们把这个区间能扩展到的最右端看做一个父子关系。
从l到l+mx这个过程我们可以看做找到一个祖先是大于等于l+mx的。
这个过程倍增实现就行。
然后通过贪心可以发现每个区间参与的答案相差都不超过1的。
那么我们求出第一个区间参与的答案。
以后的答案我们分别用前面的答案-1,0,+1来验证即可
代码实现:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int L[210000],R[210000],l[210000],r[210000],A[410000];
bool cmp(int a,int b) {return a<b;}int n,m,len;
int pos(int x) {
int l=1,r=len,mid,ans=0;
while(l<=r) {
mid=(l+r)/2;
if(A[mid]>=x) {
if(A[mid]==x)ans=mid;
r=mid-1;
}else l=mid+1;
}return ans;
}
struct node {int l,r;}s[410000];int mx[21][1100000],bin[21];
bool cmp1(node n1,node n2) {return n1.l<n2.l;}
int jump(int x,int k) {for(int i=20;i>=0;i--)if((k&bin[i]))x=mx[i][x];return x;}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {scanf("%d%d",&L[i],&R[i]);A[++len]=L[i];A[++len]=R[i];}
sort(A+1,A+1+len,cmp);int mmax=0;
for(int i=1;i<=n;i++) {l[i]=pos(L[i]);r[i]=pos(R[i]);mmax=max(mmax,max(l[i],r[i]));}
len=0;
for(int i=1;i<=n;i++) {
if(l[i]>r[i])r[i]+=mmax;
s[++len].l=l[i];s[len].r=r[i];
s[++len].l=l[i]+mmax;s[len].r=min(r[i]+mmax,2*mmax);
}sort(s+1,s+1+len,cmp1);
int t=1,F=0;memset(mx,0,sizeof(mx));
for(int i=1;i<=2*mmax;i++) {
while(s[t].l<=i&&t<=2*n) {F=s[t].r;t++;}
mx[0][i]=F;
}
bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]*2;
for(int j=1;j<=20;j++)for(int i=1;i<=2*mmax;i++)mx[j][i]=mx[j-1][mx[j-1][i]];
int ans;
for(ans=0;ans<n;ans++)if(jump(r[1],ans)>=l[1]+mmax) break;
printf("%d",ans+1);
for(int i=2;i<=n;i++) {
if(jump(r[i],ans-1)>=l[i]+mmax)printf(" %d",ans);
else if(jump(r[i],ans)>=l[i]+mmax)printf(" %d",ans+1);
else printf(" %d",ans+2);
}
return 0;
}