[BZOJ4444] [Luogu 4155] [LOJ 2007] [SCOI2015]国旗计划(倍增)

[BZOJ4444] [Luogu 4155] [LOJ 2007] [SCOI2015]国旗计划(倍增)

题面

题面较长,略

分析

首先套路的断环为链。对于从l到r的环上区间,若l<=r,我们把它断成两个区间\([l,r],[l+M,r+M]\),否则断成\([l,r+M],[l+M,r+M+M]\)(断成\([l+M,2M]\)也可以)

然后定义从区间[l,r]走到另一个与它相交的区间为1“步”。那么我们可以预处理出区间i走j步能够到达的区间右端点的最大值。注意到区间互不包含,先把区间按左端点为第一关键字,右端点为第二关键字排序。对于区间i,我们找到满足\(l_i<l_j \leq r_i\)的最大\(l_j\),那么\(r_j\)就是走1步能到达的最大区间右端点。因为\(l_j>l_i\),所以\(r_j>r_i\),否则区间j就会被i包含。由于排序过,j显然有单调性,双指针扫一遍就可以了。

	sort(a+1,a+1+sz);
	int ptr=1;
	for(int i=1;i<=sz;i++){
		while(ptr<sz&&a[ptr+1].l<=a[i].r) ptr++;
		if(ptr!=i) anc[i][0]=ptr;
	}

但是枚举走j步依然是\(O(n^2)\)的,可以用倍增优化。\(anc[i][j]\)表示区间i走j步到达的右端点最大的区间编号。这个可以\(O(n \log n)\)预处理。

查询的时候从i开始跳,一直跳到\(r_{anc[i][j]}\geq l_i+M\)为止,需注意边界条件

int query(int x){
	int ans=1;
	int r=a[x].l+len; //注意边界,比如3->5,5->1,1->3.必须要跳回原点3,所以是+len而不是+len-1 
	for(int i=log2n;i>=0;i--){
		if(anc[x][i]!=0&&a[anc[x][i]].r<=r){//如果右端点<=i+M,就继续跳
			ans+=(1<<i); 
			x=anc[x][i];
		}
	}
	if(anc[x][0]&&a[x].r<r){//上面求的是右端点<=i+M,可能跳到了<i+M的某一个位置,再跳一步就超过i+M,这种情况也是合法的。特判一下。
		ans++;
		x=anc[x][0];
	}
	return ans; //保证一定有解,所以不用判断a[x].r是否>=r
}

代码

#include<iostream>
#include<cstdio>
#include<cstring> 
#include<cmath>
#include<algorithm> 
#define INF 0x3f3f3f3f
#define maxn 2000000
#define maxlogn 25
using namespace std;
int n,len;
struct seg{
	int l;
	int r;
	int id;
	seg(){
		
	}
	seg(int _l,int _r,int _id){
		l=_l;
		r=_r;
		id=_id;
	}
	friend bool operator  < (seg p,seg q){
		if(p.l==q.l) return p.r<q.r;
		else return p.l<q.l;
	}
}a[maxn+5]; 
int sz;
int log2n; 
int ans[maxn+5];
int anc[maxn+5][maxlogn+5];

int query(int x){
	int ans=1;
	int r=a[x].l+len; //注意边界,比如3->5,5->1,1->3.必须要跳回原点3,所以是+len而不是+len-1 
	for(int i=log2n;i>=0;i--){
		if(anc[x][i]!=0&&a[anc[x][i]].r<=r){
			ans+=(1<<i); 
			x=anc[x][i];
		}
	}
	if(anc[x][0]&&a[x].r<r){
		ans++;
		x=anc[x][0];
	}
	return ans; 
}
int main(){
	int l,r;
	scanf("%d %d",&n,&len);
	log2n=log2(n*2);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&l,&r);
		if(l<=r){
			a[++sz]=seg(l,r,i);
			a[++sz]=seg(l+len,r+len,i+n);
		}else{
			a[++sz]=seg(l,r+len,i);
			a[++sz]=seg(l+len,r+len+len,i+n);
		}
	}
	sort(a+1,a+1+sz);
	int ptr=1;
	for(int i=1;i<=sz;i++){
		while(ptr<sz&&a[ptr+1].l<=a[i].r) ptr++;
		if(ptr!=i) anc[i][0]=ptr;
	}
	for(int j=1;j<=log2n;j++){
		for(int i=1;i<=sz;i++){
			anc[i][j]=anc[anc[i][j-1]][j-1];
		}
	}
	for(int i=1;i<=sz;i++){
		if(a[i].id<=n) ans[a[i].id]=query(i);//注意要跳过(l+n,r+n),否则l+len会超过2*len导致答案错误 
	}
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}

posted @ 2019-09-14 13:25  birchtree  阅读(317)  评论(0编辑  收藏  举报