【题解】【神奇校内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

原文地址:https://www.cnblogs.com/tt66ea-blog/p/15091781.html