【bzoj4444 scoi2015】国旗计划

题目描述

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

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

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

输入输出格式

输入格式:

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

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

输出格式:

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

说明

N\leqslant 2×10^5,M<10^9,1\leqslant C_i,D_i\leqslant MN2×105,M<109,1Ci,DiM 

 

题意:一个m个点的环,给出n个环上的区间(可能跨越1),求出所有 必须使用区间i而将所有的m都覆盖的最小区间数 ansi;

题解:
①如果不是环,不是多个询问就是一个贪心

②是环的话断环为链,倍长,把跨越1的拆成三分,其他的拆成两份;同样维护j位置及其左边右端点的最大值(有点绕),每次尽量往前跳; 对于ansi相当于规定了贪心的起点为i的左端点;

③实现具体可以倍增做,但是%%%Claris,对于一个坐标只会有一个最大值,意思就是所有的最大值形成一个以末尾为根的树结构,从后往前dfs遍历,用一个栈存下当前路径上的元素,预先处理没有必选区间的区间覆盖最小值L,所有的ansi和L相差不会超过1,每次向上查L,L+1个元素是否覆盖满记录答案;

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=400010;
 6 int n,m,id[N],ans[N],L=-1,tot,q[N<<1],t,hd[N<<1],nt[N<<1],sub[N],x[N>>1],y[N>>1],f[N<<1];
 7 char gc(){
 8     static char *p1,*p2,s[1000000];
 9     if(p1==p2) p2=(p1=s)+fread(s,1,1000000,stdin);
10     return (p1==p2)?EOF:*p1++;
11 }
12 int rd(){
13     int x=0; char c=gc();
14     while(c<'0'||c>'9') c=gc();
15     while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=gc();
16     return x;
17 }
18 void up(int&x,int y){if(x<y) x=y;}
19 void dfs(int u){
20     q[++t]=u;
21     if(u<=tot) for(int i=L;;i++)if(q[t-i]>=u+tot){ans[u]=i;break;}
22     for(int i=hd[u];i;i=nt[i]) dfs(i); t--;
23 }
24 int main()
25 {    freopen("bzoj4444.in","r",stdin);
26     freopen("bzoj4444.out","w",stdout);
27     n=rd(); m=rd(); sub[++tot] = 1; sub[++tot] = m;
28     for(int i=1;i<=n;i++) sub[++tot]=x[i]=rd(),sub[++tot]=y[i]=rd();    
29     sort(sub+1,sub+tot+1); tot = unique(sub+1,sub+tot+1) - sub - 1; 
30     for(int i=1;i<=n;i++){
31         id[i] = x[i] = lower_bound(sub+1,sub+tot+1,x[i]) - sub;
32         y[i] = lower_bound(sub+1,sub+tot+1,y[i]) - sub;
33         if(x[i]<=y[i]) up(f[x[i]],y[i]),up(f[x[i]+tot],y[i]+tot);
34         else up(f[1],y[i]),up(f[x[i]],y[i]+tot),up(f[x[i]+tot],2*tot); 
35     }
36     for(int i=1;i<=2*tot;i++) up(f[i],f[i-1]);
37     for(int i=1;i<=tot;i=f[i])L++;
38     for(int i=1;i<2*tot;i++) nt[i]=hd[f[i]],hd[f[i]]=i;
39     dfs(2*tot);
40     for(int i=1;i<=n;i++) printf("%d ",ans[id[i]]);
41     return 0;
42 }//by tkys_Austin;

 

 

 

转载于:https://www.cnblogs.com/Paul-Guderian/p/8692687.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值