1. 程式人生 > 其它 >【題解】【神奇校內PION模擬賽】國旗計劃

【題解】【神奇校內PION模擬賽】國旗計劃

神奇校內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; }

Author: tt66ea

Created: 2021-08-02 週一 21:19

Validate