bzoj4444 & 洛谷4155 SCOI2015 国旗计划 贪心、倍增、断环成链

题目链接:
bzoj4444
洛谷4155

Description
A国正在开展一项伟大的计划——国旗计划。这项计划的内容是边防战士手举国旗环绕边境线奔袭一圈。这项计划需要多名边防战士以接力的形式共同完成,为此,国土安全局已经挑选了N名优秀的边防战上作为这项计划的候选人。
A国幅员辽阔,边境线上设有M个边防站,顺时针编号1至M。每名边防战士常驻两个边防站,并且善于在这两个边防站之间长途奔袭,我们称这两个边防站之间的路程是这个边防战士的奔袭区间。n名边防战士都是精心挑选的,身体素质极佳,所以每名边防战士的奔袭区间都不会被其他边防战士的奔袭区间所包含。
现在,国十安全局局长希望知道,至少需要多少名边防战士,才能使得他们的奔袭区间覆盖全部的边境线,从而顺利地完成国旗计划。不仅如此,安全局局长还希望知道更详细的信息:对于每一名边防战士,在他必须参加国旗计划的前提下,至少需要多少名边防战士才能覆盖全部边境线,从而顺利地完成国旗计划。

Input
第1行,包含2个正整数N,M,分别表示边防战士数量和边防站数量。
随后n行,每行包含2个正整数。其中第i行包含的两个正整数 C i C_i Ci D i D_i Di分别表示i号边防战士常驻的两个边防站编号, C i C_i Ci号边防站沿顺时针方向至 D i D_i Di号边防站力他的奔袭区间。数据保证整个边境线都是可被覆盖的。
Output
输出数据仅1行,需要包含n个正整数。其中,第 j j j个正整数表示 j j j号边防战士必须参加的前提下至少需要多少名边防战士才能顺利地完成国旗计划
数据范围: n &lt; = 2 ∗ 1 0 5 n&lt;=2*10^5 n<=2105

先断环成链qwq
把所有边防战士的起点和终点映射到新的链上,并按照起点从小到大排序。
因为区间不会互相包含,所以按照起点从小到大排序后终点坐标也是从小到大排的qwq
所以得到贪心结论:如果第 x x x个边防战士必须参加,且当前还没有包含整个区间,那么下一个参加的边防战士与 x x x的区间相交且左端点尽可能大
因此得到一个 O ( n 2 ) O(n^2) O(n2)的算法: O ( n ) O(n) O(n)枚举哪一个边防战士必须参加, O ( n ) O(n) O(n)暴力扫得到答案。
前面 O ( n ) O(n) O(n)显然不能再优化了(因为每个边防战士必须参加的答案都要求出)
所以考虑优化后面部分:
因为这里是贪心,所以每次都是尽量往右边跳。
倍增优化,令 j m p [ x ] [ i ] jmp[x][i] jmp[x][i]表示从 x x x向右跳 2 i 2^i 2i次所到达的线段,暴力 O ( l o g n ) O(logn) O(logn)查询即珂qwq

毒瘤代码

#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
inline void write(const int x) {
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
const int Size=400005;
int n,m;
struct Segment {
	int l,r,id;
} w[Size];
inline bool comp(Segment x,Segment y) {
	if(x.l!=y.l)	return x.l<y.l;
	return x.r<y.r;
}
int jmp[Size][21];
int Query(int x) {
	int ans=0,r=w[x].l+n;
	for(re i=20; i>=0; i--) {
		if(w[jmp[x][i]].r<r) {
			x=jmp[x][i];
			ans|=1<<i;
		}
	}
	return ans+2;
}
int ans[Size];
int main() {
	w[0].l=2e9;
	m=read();
	n=read();
	for(re i=1; i<=m; i++) {
		w[i].l=read();
		w[i].r=read();
		w[i].id=i;
		if(w[i].l>w[i].r) {
			w[i].r+=n;
		}
		int id=i+m;
		w[id].l=w[i].l+n;
		w[id].r=w[i].r+n;
		w[id].id=id;
	}
	int maxm=m<<1,ptr=1;
	sort(w+1,w+1+maxm,comp);
	for(re i=1; i<=maxm; i++) {
		while(ptr<maxm && w[ptr+1].l<=w[i].r)	ptr++;
		jmp[i][0]=ptr;
	}
	for(re j=1; j<=20; j++) {
		for(re i=1; i<=maxm; i++) {
			jmp[i][j]=jmp[jmp[i][j-1]][j-1];
		}
	}
	for(re i=1; i<=maxm; i++) {
		if(w[i].id<=m) {
			ans[w[i].id]=Query(i);
		}
	}
	for(re i=1; i<=m; i++) {
		write(ans[i]);
		putchar(' ');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值