神奇校内POIN模拟赛 国旗计划



神奇校内POIN模拟赛 国旗计划



1 题外话



高队,yyds



2 sol



先把n的环拆成\(2n\) 的链

由于区间之间没有包含关系,将可选区间按左端点排序后右端点单调递增

可以二分找到左端点在当前区间内的右端点最大的区间,每次如此贪心选即可

考虑到时间复杂度\(nlogn\) 的要求,考虑倍增,\(f_{i,j}\) 表示从\(j\) 位置选择\(2^i\) 段区间最远能覆盖到哪

倍增求解,最后加上差的最后一段和本身一段总计2个区间即可



3 code




#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=200010;

inline void read(int &x) {
x=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if (ch=='-') {
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}

struct note {
int l,r;
int id;
};

bool operator < (const note &x,const note &y) {
return x.l<y.l;
}

note a[N<<1];
int n,m;
int fa[N<<1][20];
int ans[N];

inline int jump(int x,int to_x) {
int res=0;
for(int i=19;i+1;i--) {
if (fa[x][i]&&a[fa[x][i]].r<a[to_x].l+m) {
res+=(1<<i);
x=fa[x][i];
}
}
return res+2;
}

int main() {
read(n),read(m);
for(int i=1;i<=n;i++) {
read(a[i].l),read(a[i].r);
if (a[i].r<a[i].l) {
a[i].r+=m;
}
a[i+n].l=a[i].l+m;
a[i+n].r=a[i].r+m;
a[i].id=i;
}
sort(a+1,a+n+n+1);
for(int i=1;i<=n+n;i++) {
int l=i+1,r=n+n;
while(l<r) {
int mid=(l+r+1)>>1;
if (a[mid].l<=a[i].r) {
l=mid;
} else {
r=mid-1;
}
}
if (l!=n+n+1) {
fa[i][0]=l;
}
}
for(int i=1;i<=19;i++) {
for(int j=1;j<=n*2;j++) {
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
for(int i=1;i<=n;i++) {
ans[a[i].id]=jump(i,i);
}
for(int i=1;i<=n;i++) {
printf("%d ",ans[i]);
}
return 0;
}