BZOJ 4444: [Scoi2015]国旗计划

Posted 人间失格

tags:

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

Description

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

 

Input

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

 

Output

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

 

Sample Input

4 8
2 5
4 7
6 1
7 3

Sample Output

3 3 4 3

HINT

 

 n≤2×10^5,M< 10^9,1≤Ci,Di≤M

 
题解:
  感觉这个题目十分巧妙。
  首先我们先断环为链,那么对于i,我们只要走到i+m就走完了一周。
  首先考虑预处理出数组f[i]表示和i这个区间有交并且右端点最远的区间编号,这个按照有断点排序之后就可以On搞出来,当然这也是因为题目没有相互包含的区间的关系。
  那么考虑转化图论问题,从i向fi连边,那么显然就会形成一棵树,我们只要不断得跳爸爸,一直到区间i的有端点>=l+m就可以了,显然可以写一个倍增加速这个过程,但可以部分其实可以On做。
  想一想,如果我们从树顶,自上而下进行决策,那么就会有一个决策单调性,即:儿子的ans一定在爸爸的ans和爸爸之间,那么我们就维护一个栈,记录可能成为答案的点就可以了。
 
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define MAXN 400010
using namespace std;
struct edge{
    int first;
    int next;
    int to;
}a[MAXN*2];
struct qvjian{
    int l,r,id;
    bool operator < (const qvjian &x)const{
        return l<x.l;
    }
}q[MAXN];
int dep[MAXN],s[MAXN],tail=0;
int n,m,ans[MAXN],num=0;
 
void addedge(int from,int to){
    a[++num].to=to;
    a[num].next=a[from].first;
    a[from].first=num;
}
 
void dfs(int now,int f,int head){
    dep[now]=dep[f]+1;s[++tail]=now;
    if(q[now].id!=0){
        while(head<tail&&q[s[head+1]].r>=q[now].l+m) head++;
        ans[q[now].id]=dep[now]-dep[s[head]]+1;
    }
    for(int i=a[now].first;i;i=a[i].next){
        int to=a[i].to;
        dfs(to,now,head);
    }
    tail--;
}
 
int main()
{   
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        int l,r;
        scanf("%d%d",&l,&r),q[i].id=i;
        if(l>r) r+=m;
        q[i].l=l,q[i].r=r;
        q[i+n].l=q[i].l+m,q[i+n].r=q[i].r+m,q[i+n].id=0;
    }
    int now=1;
    sort(q+1,q+2*n+1);
    for(int i=1;i<2*n;i++){
        while(now<2*n&&q[i].r>=q[now+1].l) now++;
        addedge(now,i);
    }
    dfs(2*n,0,1);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

 

以上是关于BZOJ 4444: [Scoi2015]国旗计划的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 4444: [Scoi2015]国旗计划

bzoj 4444: [Scoi2015]国旗计划

4444: [Scoi2015]国旗计划

4444: [Scoi2015]国旗计划|贪心|倍增

4444: [Scoi2015]国旗计划|贪心|倍增

LibreOJ #2007. 「SCOI2015」国旗计划