[SCOI2015]国旗计划 环上倍增

题目

[SCOI2015]国旗计划(https://ac.nowcoder.com/acm/problem/20302)

题目描述

A国正在开展一项伟大的计划——国旗计划。这项计划的内容是边防战士手举国旗环绕边境线奔袭一圈。这 项计划需要多名边防战士以接力的形式共同完成,为此,国土安全局已经挑选了N名优秀的边防战上作为这 项计划的候选人。
A国幅员辽阔,边境线上设有M个边防站,顺时针编号1至M。每名边防战士常驻两个边防站,并且善于在这两个边防站之间长途奔袭,我们称这两个边防站之间的路程是这个边防战士的奔袭区间。n名边防战士 都是精心挑选的,身体素质极佳,所以每名边防战士的奔袭区间都不会被其他边防战士的奔袭区间所包含。
现在,国十安全局局长希望知道,至少需要多少名边防战士,才能使得他们的奔袭区间覆盖全部的边境线, 从而顺利地完成国旗计划。不仅如此,安全局局长还希望知道更详细的信息:对于每一名边防战士,在他必须参加国旗计划的前提下,至少需要多少名边防战士才能覆盖全部边境线,从而顺利地完成国旗计划。

输入描述:

第1行,包含2个正整数N,M,分别表示边防战士数量和边防站数量。
随后n行,每行包含2个正整数。其中第i行包含的两个正整数Ci、Di分别表示i号边防战士常驻的两个边防站编号,Ci号边防站沿顺时针方向至Di号边防站力他的奔袭区间。
数据保证整个边境线都是可被覆盖的。

输出描述:

输出数据仅1行,需要包含n个正整数。其中,第j个正整数表示j号边防战士必须参加的前提下至少需要多少名边防战士才能顺利地完成国旗计划

示例1

输入
4 8
2 5
4 7
6 1
7 3
输出
3 3 4 3

思路

我们发现一个性质。没有没有区间相互包含,那么把每个区间按L排序,R一定的递增的。

那么有得到一个性质。第i个区间的下一个区间的最佳匹配区间k。一定是a[k].L<=a[i].R并且a[k].L最大。这样a[k].R就最大,下次跑的更远。
所以每个区间我们都可以二分或者递推得到他的最佳匹配区间。

对于环我们肯定要拆成链。

for(int i=1; i<=n; i++){
      scanf("%d%d", &a[i].L, &a[i].R);
      a[i].id=i;
      if(a[i].R<a[i].L) a[i].R+=m;
      a[n+i].L=a[i].L+m; a[n+i].R=a[i].R+m;
}

对于一个区间i最小需要k个人,那么对于\(a[k+i-1].R-a[i].L>=m\)
我们考虑倍增来做。\(f[i][j]:j经过2^i个区间到达的区间\)

for(int i=1; i<=n; i++){
    int pos=i;
    for(int k=20; k>=0; k--){
        if(f[pos][k]&&a[f[pos][k]].R<a[i].L+m){
            ans[a[i].id]+=(1<<k); pos=f[pos][k];
        }
    }
}

最后ans[i]+2,因为一个是自己这个区间。另外一个是倍增的是<a[i].L+m的最进的区间。那么还需要跳一个区间。
题目说了一定满足能够跑一圈,就不用再判断不满足了。

#include <bits/stdc++.h>
using namespace std;

struct Node{
    int L, R, id;
}a[400005];

int f[400005][21];
int ans[400005];
int main(){

    int n, m; scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++){
        scanf("%d%d", &a[i].L, &a[i].R);
        a[i].id=i;
        if(a[i].R<a[i].L) a[i].R+=m;
        a[n+i].L=a[i].L+m; a[n+i].R=a[i].R+m;
    }
    sort(a+1, a+2*n+1, [](Node &a, Node &b){return a.L<b.L;});
    for(int i=1; i<=2*n; i++){
        int l=i+1, r=2*n;
        while(l<=r){
            int mid=l+r>>1;
            if(a[mid].L>=a[i].L&&a[mid].L<=a[i].R){
                f[i][0]=mid; l=mid+1;
            }else{
                r=mid-1;
            }
        }
    }
    for(int k=1; k<=20; k++){
        for(int i=1; i<=2*n; i++){
            f[i][k]=f[f[i][k-1]][k-1];
        }
    }
    for(int i=1; i<=n; i++){
        int pos=i;
        for(int k=20; k>=0; k--){
            if(f[pos][k]&&a[f[pos][k]].R<a[i].L+m){
                ans[a[i].id]+=(1<<k); pos=f[pos][k];
            }
        }
    }
    for(int i=1; i<=n; i++) printf("%d%c", ans[i]+2, i==n?'\n':' ');

    return 0;
}

posted @ 2020-12-07 11:34  liweihang  阅读(103)  评论(0编辑  收藏  举报
Live2D