zoukankan      html  css  js  c++  java
  • BZOJ4444: [Scoi2015]国旗计划

    BZOJ4444: [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

    题解Here!

    这题求环上最少线段覆盖整个区间。

    首先,将环断成链,倍长,这是基本套路。

    然后贪心一下:

    对于一个战士 i ,如果选了他,那么下一个战士 j 一定是所有左端点小于等于 i 的右端点的战士中右端点最大的一个战士,这样是最优的。

    怎么找到一个战士的下一个是哪一个呢?

    预处理+二分查找一下就可以了。

    然后我们就直接一个一个的找就可以了。

    但是这样会被极限数据卡成 O(n2) 。。。怎么办?

    我们想起,优化暴力的一种最常用的方法是——倍增!

    (什么优化贪心的模拟退火就算了吧。。。)

    于是处理一下倍增数组,然后倍增跳即可。

    注:被高级数据结构洗礼的我竟然一个上午才1A,药丸。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 400010
    #define MAX 2000000000
    using namespace std;
    int n,m,top;
    int ans[MAXN],f[MAXN][20];
    struct node{
    	int l,r,id;
    }a[MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    bool cmp(const node &p,const node &q){
    	if(p.l==q.l)return p.r<q.r;
    	return p.l<q.l;
    }
    int half_find(int x,int l,int r){
    	int mid,s=n+1;
    	while(l<=r){
    		mid=l+r>>1;
    		if(a[mid].l<=x){s=mid;l=mid+1;}
    		else r=mid-1;
    	}
    	return s;
    }
    void step(){
    	for(int i=0;i<=19;i++)
    	for(int j=1;j<=n+1;j++)
    	f[j][i]=n+1;
    	for(int i=1;i<=n;i++)f[i][0]=half_find(a[i].r,i+1,n+1);
    	for(int i=1;i<=19;i++)
    	for(int j=1;j<=n;j++)
    	f[j][i]=f[f[j][i-1]][i-1];
    }
    void work(){
    	for(int i=1;i<=n;i++)
    	if(a[i].id&&!ans[a[i].id]){
    		int now=i;
    		for(int j=19;j>=0;j--)
    		if(a[f[now][j]].r<a[i].l+m){
    			now=f[now][j];
    			ans[a[i].id]+=(1<<j);
    		}
    	}
    	for(int i=1;i<=top;i++)printf("%d ",ans[i]+2);
    }
    void init(){
    	n=read();m=read();
    	for(int i=1;i<=n;i++){
    		a[i].l=read();a[i].r=read();
    		a[i].id=i;
    	}
    	top=n;
    	for(int i=1;i<=n;i++){
    		if(a[i].l>a[i].r)a[i].r+=m;
    		else{
    			top++;
    			a[top].l=a[i].l+m;a[top].r=a[i].r+m;a[top].id=i;
    		}
    	}
    	swap(n,top);
    	sort(a+1,a+n+1,cmp);
    	a[n+1].r=MAX;
    	step();
    }
    int main(){
    	init();
    	work();
    	return 0;
    }
    
  • 相关阅读:
    jbpm入门样例
    MinGW 介绍
    Linux守护进程的编程实现
    完毕port(CompletionPort)具体解释
    Linux makefile 教程 很具体,且易懂
    mysql数据文件迁移到新的硬盘分区的方法
    winform正在使用dsoframer迅速&quot;Unable to display the inactive document.Click here to reacitive the document.&quot;
    Android学习路径(七)建立Action Bar
    FreeBSD包管理
    BZOJ 1096 ZJOI2007 仓库建设 边坡优化
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9361979.html
Copyright © 2011-2022 走看看