洛谷 P4155 国旗计划

[SCOI2015] 国旗计划

题目描述

A 国正在开展一项伟大的计划 —— 国旗计划。这项计划的内容是边防战士手举国旗环绕边境线奔袭一圈。这项计划需要多名边防战士以接力的形式共同完成,为此,国土安全局已经挑选了 N N N 名优秀的边防战上作为这项计划的候选人。

A 国幅员辽阔,边境线上设有 M M M 个边防站,顺时针编号 1 1 1 M M M。每名边防战士常驻两个边防站,并且善于在这两个边防站之间长途奔袭,我们称这两个边防站之间的路程是这个边防战士的奔袭区间。 N N N 名边防战士都是精心挑选的,身体素质极佳,所以每名边防战士的奔袭区间都不会被其他边防战士的奔袭区间所包含。

现在,国十安全局局长希望知道,至少需要多少名边防战士,才能使得他们的奔袭区间覆盖全部的边境线,从而顺利地完成国旗计划。不仅如此,安全局局长还希望知道更详细的信息:对于每一名边防战士,在他必须参加国旗计划的前提下,至少需要多少名边防战士才能覆盖全部边境线,从而顺利地完成国旗计划。

输入格式

第一行,包含两个正整数 N , M N,M N,M,分别表示边防战士数量和边防站数量。

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

输出格式

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

样例 #1

样例输入 #1

4 8
2 5
4 7
6 1
7 3

样例输出 #1

3 3 4 3

提示

N ⩽ 2 × 1 0 5 , M < 1 0 9 , 1 ⩽ C i , D i ⩽ M N\leqslant 2×10^5,M<10^9,1\leqslant C_i,D_i\leqslant M N2×105,M<109,1Ci,DiM

倍增加贪心
使用到的技术有:
一、化圆为线
为了保持原来的首尾关系,需要把原来的圆圈复制再相接

二、贪心
考虑该区间与下一个区间的关系,先将区间按照左端点排序,因为题目说明各个区间不存在完全包含关系,所以,下一区间的左端点越大,则右端点越大,所以选择左端点小于该区间的右端点的区间,并且要求左端点尽量大

三、倍增
为了进行高效的查询,可以倍增法预计算出一些跳板,快速找到后面的区间

  g o [ s ] [ i ] = g o [ g o [ s ] [ i − 1 ] ] [ i − 1 ] \ go[s][i] = go[go[s][i-1]][i-1]  go[s][i]=go[go[s][i1]][i1]
  g o [ s ] [ i ] 表示从第 s 个区间出发,走 2 i 个最优区间后到达的区间 \ go[s][i]表示从第s个区间出发,走2^i个最优区间后到达的区间  go[s][i]表示从第s个区间出发,走2i个最优区间后到达的区间

#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 1;
int n, m;
struct warrior{
    int id, L, R;
    bool operator < (const warrior b) const{
        return L < b.L;
    }
} w[N * 2];
int n2; // n2实际上是战士的数量的两倍,因为下面把环拆下来并且加倍成为一条链
int go[N][20];
void init(){
    int nxt = 1;//下一个战士
    for (int i = 1; i <= n2; i++){ //从第一个战士遍历到最后一个战士,用贪心法求得每个区间的下一个最优区间
        while(nxt <= n2 && w[nxt].L <= w[i].R){ //从第nxt个战士开始向后查找,找到最后一个满足nxt的左区间小于i的右区间的战士
            nxt++;                              //因为题目中说明,没有战士的区间被完全包含,所以,最后一名 L < R 一定意味着该区间右区间最大!
        }
        go[i][0] = nxt - 1;     //第i个区间的下一个最优的区间应该是 nxt - 1,因为计算时nxt会多加一个1
    }
    for (int i = 1; (1 << i) <= n; i++)  //倍增:(1 << i) = 1,2,4,8...共logn次 ,i是指数
        for (int s = 1; s <= n2; s++){  //用倍增法找到从 s 区间跳 2^i 个最优区间到达的区间
            go[s][i] = go[go[s][i - 1]][i - 1];
        }
}
int res[N];
void get_ans(int x){ //从第 x 个战士出发
    int len = w[x].L + m;
    int cur = x, ans = 1;
    for (int i = log2(N); i >= 0; i--)  {//从最大的i开始找: 2^i = N
        int pos = go[cur][i];
        if(pos && w[pos].R < len){
            ans += 1 << i;  //累加跳过的区域
            cur = pos;
        }
    }
    res[w[x].id] = ans + 1;
}

int main(){
    scanf(" %d %d", &n, &m);
    for (int i = 1; i <= n; i++){//记录战士的顺序
        w[i].id = i;
        scanf("%d %d", &w[i].L, &w[i].R);
        if(w[i].R < w[i].L)
            w[i].R += m;//变环为链
    }
    sort(w + 1, w + n + 1);
    n2 = n;
    for (int i = 1; i <= n; i++){//拆环加倍
        n2++;
        w[n2].L = w[i].L + m;
        w[n2].R = w[i].R + m;
    }
    init();
    for (int i = 1; i <= n;i++)
        get_ans(i);//逐个计算每个战士
    for (int i = 1; i <= n;i++)
        printf("%d ", res[i]);
    return 0;
}
  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"c洛谷国旗"是一个题目,在洛谷在线评测系统上。该题目的要求是给定一个由N×M个小方块组成的棋盘布,每个方块是白色、蓝色或红色。你需要按照某国法律规定的国旗规则,将布涂成合法的国旗。根据法律规定,国旗的布局应该是从最上方若干行(至少一行)的格子全部是白色的,接下来若干行(至少一行)的格子全部是蓝色的,剩下的行(至少一行)全部是红色的。你需要通过对格子上颜色的涂改来实现这个目标。具体来说,你需要在一些格子上涂上新的颜色来盖住之前的颜色。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [洛谷 P3392 涂国旗](https://blog.csdn.net/m0_60777643/article/details/122149321)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [洛谷-P3392 涂国旗](https://blog.csdn.net/weixin_43098069/article/details/107484242)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值