4444: [Scoi2015]国旗计划

解析[Scoi2015]国旗计划竞赛题,探讨如何通过贪心算法确定最少参与人员,实现边境线全覆盖。介绍了解题思路及关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4444: [Scoi2015]国旗计划

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 246   Solved: 111
[ Submit][ Status][ Discuss]

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

[ Submit][ Status][ Discuss]



一个贪心,假设你当前已经覆盖了区间[l,r],要选择的下一个战士,一定是能覆盖到的右端点越右端越好

令f[r]为当前覆盖到的右端点为r,选择下一位战士后,能往右覆盖到的最右端的值

因为题中给的区间不相交,所以只要一直把右端点往右边推就好了

事实上原题中给的是一个环

如果我们将[1,m]连写两遍,那么每个战士的区间都可以以它左端点起向右无脑扩一条链出来

分块下维护要走多少次能够跑出去。。


记得离散坐标额。。

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 4E5 + 20;
const int maxm = 2*maxn;

int n,m,Sqrt,cnt,cur = 1,L[maxn],R[maxn],belong[maxm],a[maxn],
	from[maxm],to[maxm],f[maxm],Nex[maxm],w[maxm];

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> n >> m; int tot = 0;
	for (int i = 1; i <= n; i++) {
		scanf("%d%d",&L[i],&R[i]);
		a[++tot] = L[i]; a[++tot] = R[i];
	}
	sort(a + 1,a + tot + 1);
	for (int i = 2; i <= tot; i++)
		if (a[i] != a[i-1])
			a[++cur] = a[i];
	
	m = cur; Sqrt = sqrt(2*m);
	for (int i = 1; i <= n; i++) {
		L[i] = lower_bound(a + 1,a + cur + 1,L[i]) - a;
		R[i] = lower_bound(a + 1,a + cur + 1,R[i]) - a;
		if (L[i] <= R[i]) {
			f[L[i]] = max(f[L[i]],R[i]);
			f[L[i]+m] = max(f[L[i]+m],R[i]+m);
		} 
		else {
			f[L[i]] = max(f[L[i]],R[i]+m);
			f[L[i]+m] = 2*m+1;
			f[1] = max(f[1],R[i]);
		}
	} 
	
	int A = 1,B = Sqrt; 
	from[cnt = 1] = 1;
	for(int i = 1; i <= 2*m; i++) {
		f[i] = max(f[i],f[i-1]);
		if (i > B) {
			A += Sqrt; to[cnt] = B;
			B += Sqrt; ++cnt;
			from[cnt] = A;
		}
		belong[i] = cnt;
	}
	to[cnt] = 2*m; belong[2*m+1] = cnt+1;
	
	for (int i = 2*m; i; i--) {
		int j = f[i]; 
		if (j > to[belong[i]]) {Nex[i] = j; w[i] = 1;}
		else Nex[i] = Nex[j],w[i] = w[j] + 1;
	}
	
	for (int i = 1; i <= n; i++) {
		int ans = 0;
		int l = L[i],r,pos;
		if (L[i] <= R[i]) r = m + L[i],pos = R[i];
		else r = m + L[i],pos = R[i] + m;
		for (;;) {
			if (belong[Nex[pos]] < belong[r]) 
				ans += w[pos],pos = Nex[pos];
			else break;
		}
		while (pos < r) 
			++ans,pos = f[pos];
		if (i < n) printf("%d ",ans + 1);
		else cout << ans + 1;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值