[luogu] P4155 [SCOI2015]国旗计划(贪心)

Posted hhh1109

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[luogu] P4155 [SCOI2015]国旗计划(贪心)相关的知识,希望对你有一定的参考价值。

P4155 [SCOI2015]国旗计划

题目描述

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

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

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

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

输入样例#1: 复制

4 8
2 5
4 7
6 1
7 3

输出样例#1: 复制

3 3 4 3

说明

(Nleqslant 2×10^5,M<10^9,1leqslant C_i,D_ileqslant M)

题解

有一个(O(nlogn))的暴力做法。
那就是先预处理出每一个人下一步接力的人。
贪心在于题意明确了不包含的规则,即(l_i<l_j),则(r_i<r_j)
所以处理下一步接力的人可以排序以后(O(n))找,符合单调性。
然后倍增暴力搞。
那么这就是个傻逼题。
我的代码有问题可以被hack,参考的是ysner的。

还有一个(O(n))的做法。
太懒了,这个就不打了,我随意口糊的,看相当于不看
这是我没看题解前想到的-----答案一定只有两个这个是可以确定的。
要么是最优答案,要么是最优答案(+1).
但是问题出在怎么找出当前的人是否参与了最优的方案。
我们当前可以按不包含规则建一棵树。
然后按(1)先跑一遍,求出近似最优解,它可能是,也不是。
然后对于(n)个点,枚举一遍,每遍是(3)(+1,-1,0)
验证就利用树一个儿子只有一个父亲的特点记录一下然后查询就可以了。
时间复杂度还是(O(n))

Code

注意,这个是错误代码,只是可以过而已。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=1e6+5;
struct node{
    int l,r,id;
}a[N];
int tot,n,m,ans[N],f[N][21];
int read(){
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

bool cmp(node a,node b){return a.l<b.l;}

int query(int x){
    int end=a[x].l+m,now=x,sum=2;
    for(int i=20;i>=0;i--){
        if(f[now][i]&&a[f[now][i]].r<end)now=f[now][i],sum+=(1<<i);
    }return sum;
}

int main(){
    tot=n=read();m=read();
    for(int i=1;i<=n;i++){
        a[i].l=read();a[i].r=read();a[i].id=i;
        if(a[i].l>a[i].r)a[i].r+=m;
        else a[++tot].l=a[i].l+m,a[tot].r=a[i].r+m;
    }sort(a+1,a+tot+1,cmp);a[tot+1].r=2e9;
    int now=1;
    for(int i=1;i<=tot;i++){
        while(now<=tot&&a[now+1].l<=a[i].r)now++;
        f[i][0]=now;
    }
    for(int j=1;j<=20;j++)
        for(int i=1;i<=tot;i++){
        f[i][j]=f[f[i][j-1]][j-1];
    }
    for(int i=1;i<=tot;i++)if(a[i].l<=m)ans[a[i].id]=query(i);
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    return 0;
}

以上是关于[luogu] P4155 [SCOI2015]国旗计划(贪心)的主要内容,如果未能解决你的问题,请参考以下文章

[SCOI2015]国旗计划

BZOJ4444[Scoi2015]国旗计划 双指针+倍增

BZOJ 4444: [Scoi2015]国旗计划

bzoj 4444: [Scoi2015]国旗计划

4444: [Scoi2015]国旗计划

bzoj4444[Scoi2015]国旗计划