大学生程序设计邀请赛(华东师范大学)-D-线段树

链接:点击打开链接


题解:二分预处理出来每个点单次传染到的左右范围,再用线段树维护每个点最终的范围最小值、最大值,利用线段树不断扩大范围并更新。随机一个 1 ~ n 的序列利用线段树更新,这样速度会变得很快,或其他优化方法均可(这里采用sin函数排序差不多就无序了= =)。还有就是注意题目是按原来的i输出,我因为这个被卡了一下午,MDZZ。


代码:

#include<cstdio>  
#include<cstring>  
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;  
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define inf 0x3f3f3f3f
const int mx=1e5+10;
typedef long long ll;
int n,sum[mx<<2][2],a[mx],ans[mx],b[mx]; 
using namespace std;
struct sec{
	int l,r;
	bool operator < (sec A)const{
	     return l<A.l;
	} 
	sec() {}
	sec(int ll,int rr){  l=ll,r=rr; }
}s[mx],po[mx];
bool cmp(int a,int b){
	 return sin(1.0*a)<sin(1.0*b);
}
void build(int l,int r,int rt){
	if(l==r){
		sum[rt][0]=s[l].l;
		sum[rt][1]=s[l].r;
		return ;
	}
	int m=(l+r)>>1;
	build(lson);
	build(rson);
	sum[rt][0]=min(sum[rt<<1][0],sum[rt<<1|1][0]);
	sum[rt][1]=max(sum[rt<<1][1],sum[rt<<1|1][1]);		
}
sec query(int L,int R,int l,int r,int rt){
	if(L<=l&&R>=r){
		return sec(sum[rt][0],sum[rt][1]);
	}
	int m=(l+r)>>1;
	sec num[2]={{inf,0},{inf,0}};
	if(m>=L)  num[0]=query(L,R,lson);
	if(R>m)  num[1]=query(L,R,rson);
	return sec( min(num[0].l,num[1].l) , max(num[0].r,num[1].r) );
} 
void update(int L,int R,int l,int r,int rt,int vl,int vr){
	if(l==r){
		sum[rt][0]=min(vl,sum[rt][0]);
		sum[rt][1]=max(vr,sum[rt][1]);	
		return ;	
	}
	int m=(l+r)>>1;
	if(m>=L)  update(L,R,lson,vl,vr);
	if(R>m)  update(L,R,rson,vl,vr);
	sum[rt][0]=min(sum[rt<<1][0],sum[rt<<1|1][0]);
	sum[rt][1]=max(sum[rt<<1][1],sum[rt<<1|1][1]);	
}
int main(){
	while(~scanf("%d",&n)){
		for(int i=1;i<=n;i++){
			scanf("%d%d",&s[i].l,&s[i].r);
			po[i].l=s[i].l ,po[i].r=i;
		}a[n+1]=-inf;
		sort(s+1,s+1+n);
		sort(po+1,po+1+n);
		for(int i=1;i<=n;i++)  a[i]=s[i].l;
		for(int i=1;i<=n;i++){
			int l=s[i].l,r=s[i].r;
			s[i].l=lower_bound(a+1,a+1+n,l-r)-a;
			s[i].r=lower_bound(a+1,a+1+n,l+r)-a;
			if(a[s[i].r]!=l+r) s[i].r--;			
		}
		build(1,n,1);
		for(int i=1;i<=n;i++)  a[i]=i;
		sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++){
			int id=0,t=a[i];
			sec num[2]={{t,t},{0,0}};
			while(num[0].l!=num[1].l || num[0].r!=num[1].r){
				num[id^1]=query(num[id].l,num[id].r,1,n,1);
				id^=1;
			}
			update(t,t,1,n,1,num[id].l,num[id].r);//更新t点感染范围 
			ans[po[t].r]=num[id].r-num[id].l+1;//找回原来的位置赋值 
		}printf("%d",ans[1]);
		for(int i=2;i<=n;i++) printf(" %d",ans[i]);
		puts("");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值