zoukankan      html  css  js  c++  java
  • 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

    Source

    这种环的题目考虑断环为链然后倍长来处理;

    然后我们发现每个人肯定要跑得越远越好,因为这样覆盖的区间最大,而且他所覆盖的其他人都可以帮他接力,所以不可能差;

    那么从每个点选择的接力的人只有一个,就是他覆盖的路径中能跑得最远的那个人,这个用线段树查询一下区间最大值;

    既然每个人都只有一个父亲,那么实际上移动构成了一种树的关系,然后我们要快速的处理移动的询问,我门采用倍增来加速移动即可;

    注意需要离散化,以及环倍长后的另外一边要做同样的倍增处理;

    //MADE BY QT666
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define lson x<<1
    #define rson x<<1|1
    using namespace std;
    typedef long long ll;
    const int N=500050;
    int fa[N*2][18],n,m,c[N*2],d[N*2],tr[N*8];
    int hsh[N*2],tot;
    void build(int x,int l,int r){
        if(l==r) {tr[x]=fa[l][0];return;}
        int mid=(l+r)>>1;
        build(lson,l,mid);build(rson,mid+1,r);
        tr[x]=max(tr[lson],tr[rson]);
    }
    int query(int x,int l,int r,int xl,int xr){
        if(xl>xr) return 0;
        if(xl<=l&&r<=xr) return tr[x];
        int mid=(l+r)>>1;
        if(xr<=mid) return query(lson,l,mid,xl,xr);
        else if(xl>mid) return query(rson,mid+1,r,xl,xr);
        else return max(query(lson,l,mid,xl,mid),query(rson,mid+1,r,mid+1,xr));
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
    	int x,y;scanf("%d%d",&x,&y);
    	hsh[++tot]=x,hsh[++tot]=y,hsh[++tot]=m+y,hsh[++tot]=x+m;
    	c[i]=x;if(x<y) d[i]=y;else d[i]=m+y;
        }
        sort(hsh+1,hsh+1+tot);tot=unique(hsh+1,hsh+1+tot)-hsh-1;
        for(int i=1;i<=tot;i++){
    	if(hsh[i]==0) cout<<i<<endl;
        }
        for(int i=1;i<=n;i++){
    	if(d[i]<=m){
    	    c[i]=lower_bound(hsh+1,hsh+1+tot,c[i])-hsh;
    	    d[i]=lower_bound(hsh+1,hsh+1+tot,d[i])-hsh;
    	    fa[c[i]][0]=d[i];
    	    int cc=lower_bound(hsh+1,hsh+1+tot,hsh[c[i]]+m)-hsh;
    	    int dd=lower_bound(hsh+1,hsh+1+tot,hsh[d[i]]+m)-hsh;
    	    fa[cc][0]=dd;
    	}
    	else{
    	    c[i]=lower_bound(hsh+1,hsh+1+tot,c[i])-hsh;
    	    d[i]=lower_bound(hsh+1,hsh+1+tot,d[i])-hsh;
    	    fa[c[i]][0]=d[i];
    	    int cc=lower_bound(hsh+1,hsh+1+tot,hsh[c[i]]+m)-hsh;
    	    fa[cc][0]=tot;
    	}
        }
        for(int i=1;i<=tot;i++) if(hsh[i]==0)  cout<<i<<endl;
        build(1,1,tot);
        for(int i=1;i<=n;i++){
    	fa[d[i]][0]=query(1,1,tot,c[i]+1,d[i]);
    	if(hsh[d[i]]<=m){
    	    int cc=lower_bound(hsh+1,hsh+1+tot,hsh[c[i]]+m)-hsh;
    	    int dd=lower_bound(hsh+1,hsh+1+tot,hsh[d[i]]+m)-hsh;
    	    fa[dd][0]=query(1,1,tot,cc+1,dd);
    	}
        }
        for(int j=1;j<=17;j++){
    	for(int i=1;i<=tot;i++){
    	    fa[i][j]=fa[fa[i][j-1]][j-1];
    	}
        }
        for(int i=1;i<=n;i++){
    	int ret=0,x=c[i];
    	for(int j=17;j>=0;j--){
    	    if(hsh[fa[x][j]]<hsh[c[i]]+m&&fa[x][j]){
    		ret+=(1<<j),x=fa[x][j];
    	    }
    	}
    	ret++;
    	printf("%d ",ret);
        }
        return 0;
    }
    
  • 相关阅读:
    如何阅读大型代码库?
    发现一个时隐时现的bug!
    写给开发者:记录日志的10个建议
    教你一眼认出英语单词的意思
    为什么我要使用一个20年前的IBM老键盘
    有了screen,妈妈再也不用担心我的学习啦
    一次优秀的代码提交应该包含什么?
    你需要的不是重构,而是理清业务逻辑
    Android中监听ListView滑动到底部
    Android中的Handler,Looper,Message机制
  • 原文地址:https://www.cnblogs.com/qt666/p/7625183.html
Copyright © 2011-2022 走看看