神奇校内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; }
Created: 2021-08-02 周一 21:19