BZOJ4444: [Scoi2015]国旗计划 解题报告

138 篇文章 0 订阅

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号边防战士必须参加的前提下至少需要多少名边防战士才能顺利地完成国旗计划



题解:

先离散化,下文说的 x,m 等都指离散化后的值
考虑一种朴素的做法,对于位置 i ,预处理左端点不超过i,右端点最远拉到 f(i) ,将环剖成一条长2m的链,那么对于一个点 i 的左端点x,不断跳 f(x) ,直到跳到 x+m 或它的右边
其实经过观察,可以发现 f(i) 构成了一棵以 2m 为根的树,而且易得一个东西:第 i 个点的答案和其他点的答案相差不超过1
所以预处理第一个点的答案 k ,从2m搜下去,把沿途的点放到栈里面,对于需要计算答案的点,在栈顶往下k+1层 y ,往右跳一下找解,跳的次数不会超过2次
复杂度O(nlog2n)(不算排序是 O(n)


code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<ctime>
#include<cmath>
#include<string>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<climits>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 810000;
inline void read(int &x)
{
    char c;
    while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9')
        (x*=10)+=c-'0';
}
struct node
{
    int x,y,id;
}a[maxn],b[maxn];
int n,m,num,M;
int f[maxn],ans[maxn];

bool cmp(node x,node y){return x.x<y.x;}
inline void mx(int &x,int y){if(y>x)x=y;}

struct edge
{
    int y,next;
}e[maxn];int len,first[maxn];
inline void ins(int x,int y)
{
    len++; e[len].y=y; e[len].next=first[x];first[x]=len;
}
int sta[maxn],tp,dfsta[maxn],dft;
int times[maxn];
inline void dfs(int x)
{
    sta[++tp]=x;
    if(x<=m)
    {
        int y=sta[tp-ans[1]+1]; ans[x]=ans[1]-1;
        while(y<x+m)
        {
            ans[x]++;
            y=f[y];
        }
    }
    for(int k=first[x];k;k=e[k].next) dfsta[++dft]=e[k].y;
}

int main()
{
    read(n);read(m);
    num = 0;
    for(int i=1;i<=n;i++)
    {
        read(a[i].x); read(a[i].y);
        b[++num].x=a[i].x;b[num].y=0;b[num].id=i;
        b[++num].x=a[i].y;b[num].y=1;b[num].id=i;
    }
    sort(b+1,b+num+1,cmp);
    int k=0; b[0].x=b[1].x-1;
    for(int i=1;i<=num;i++)
    {
        if(b[i].x!=b[i-1].x) k++;
        if(b[i].y==0)a[b[i].id].x=k;
        else a[b[i].id].y=k;
    }

    m=k; M=m+m;
    for(int i=1;i<=n;i++)
    {
        if(a[i].x<a[i].y)mx(f[a[i].x],a[i].y),mx(f[a[i].x+m],a[i].y+m);
        else mx(f[1],a[i].y),mx(f[a[i].x],a[i].y+m),mx(f[a[i].x+m],M);
    }
    for(int i=2;i<=M;i++) mx(f[i],f[i-1]);

    len=0;memset(first,0,sizeof first);
    for(int i=1;i<M;i++) ins(f[i],i);

    ans[1]=0;for(int i=1;i<m;i=f[i])ans[1]++;
    tp=0; 
    dfsta[dft=1]=M;
    while(dft)
    {
        int ti=dft; times[dfsta[dft]]++;
        if(times[dfsta[dft]]==2)
        {
            tp--,dft--;
            continue;
        }
        dfs(dfsta[dft]);
        if(ti==dft)tp--,dft--;
    }
    for(int i=1;i<n;i++) printf("%d ",ans[a[i].x]);
    printf("%d",ans[a[n].x]);
    //printf("\n");

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值