URAL 1019. Line Painting (线段树)

方法一:用链表存区间,每次上色的操作,依次取出所有的区间,用上色的区间去覆盖,返回新的区间。

由于每次都要遍历一遍所有的区间,比方法二慢。


方法二:离散化的线段树

先把所有区间的端点列出来,排序。

然后用线段树来做,最后将连续的白色区间合并,再计算答案。

代码写的比较乱。



方法一代码:

#define FOR(i,n) for(long long (i)=1;(i)<=(n);(i)++)
#define For(i,n) for(long long (i)=0;(i)<(n);(i)++)
using namespace std;
struct Node{
	int L,R;//横坐标 
	bool Flag;//这段是黑1,白0 
	Node *next;
}; 
Node *List; //区间链表的头指针 
int N;
Node *NewList=NULL,*NewEnd=NULL;
void Insert(int a,int b,bool v); //区间上色 
void Add(Node *T);//将T加入新链表 
Node *AddIn(int a,int b,bool v);//辅助函数,生成一个 区间的结构体。 
int main(void)
{
	while(cin>>N){
		Node *temp=new Node;List=NULL;
		temp->L=0;temp->R=1000000000;temp->Flag=0;
		temp->next=List;List=temp;
		
		For(i,N) {
			int a,b;char c;
			cin>>a>>b>>c;
			Insert(a,b,c=='b');
		}
		int Max=-1;int L=-1,R=-1;
		//搜索所有区间,找最长的区间。
		temp=List;
		while(temp){
			if(!temp->Flag) if(temp->R-temp->L>Max) Max=temp->R-temp->L,L=temp->L,R=temp->R; 
			temp=temp->next;
		}
		printf("%d %d\n",L,R);
	}
return 0;
}
void Insert(int a,int b,bool v){
	NewList=NULL;NewEnd=NULL;
	int T=0;//是否已经完成操作。 
	while(List){
		//取出一个元素 		
		Node *temp=List;
		List=List->next;
		if(T || (temp->R < a) ) {
			Add(temp); 
			continue;
		}
		//比较是否需要更改,将结果加入新链表 
		
		if(b <=temp->R){
			if(temp->Flag!=v){//分割成3区间
				Add(AddIn(temp->L,a,temp->Flag));
				Add(AddIn(a,b,v));
				Add(AddIn(b,temp->R,temp->Flag));
				T=1;continue;
			}
			else{
				Add(temp);//不操作,直接放回
				T=1;
				continue;
			}
			
		}
		else{
			if(temp->Flag!=v){
				Add(AddIn(temp->L,a,temp->Flag));
				Add(AddIn(a,temp->R,v));
				a=temp->R;
			}
			else{
				a=temp->R;
				Add(temp);
					
			}
			
		}
	}
	List=NewList;
}
void Add(Node *T){
	if(T==NULL) return;
	T->next=NULL;
	if(NewEnd){
		if(NewEnd->R==T->L){
			if(NewEnd->Flag==T->Flag){
				NewEnd->R=T->R;
				delete T;
				return;
			}
		}
		NewEnd->next=T;
		NewEnd=T;
		NewEnd->next=NULL;
	}
	else{
		NewList=T;
		NewEnd=T;
		NewEnd->next=NULL;
	}
}
Node *AddIn(int a,int b,bool v){
	if(a>=b) return NULL;
	Node *temp=new Node;
	temp->L=a,temp->R=b,temp->Flag=v;temp->next=NULL;
	return temp;
}



方法二代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
#define  inf  0x5fffffff
#define PN 10010  //点数 
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
struct P{
	int x;
	bool operator <(const P&B)const{return x<B.x;}
}p[PN];int Pn;
struct Seg{
	int l,r;
	bool v;
	void set(int l,int r){
		this->l=l;this->r=r;
	}
}s[PN];
struct Node{//点
	int v;//黑1 白0  混色-1 
}node[PN <<2];
int N;
void PushDown(int rt){
	if(~node[rt].v){//如果是纯色 
		node[rt<<1].v=node[rt].v;
		node[rt<<1|1].v=node[rt].v;
		node[rt].v=-1;
	}
}
void build(int l,int r,int rt){
	if(l==Pn) return;
	if(l==r){
		node[rt].v=0;
		return;
	}
	int m=(l+r)>>1;
	build(ls);
	build(rs);
	node[rt].v=0;
}
void insert(int L,int R,bool v,int l,int r,int rt){
	if(l==Pn) return;
	if(L <=p[l].x && p[r].x <R){
		node[rt].v=v;
		return;
	}
	if(l==r&&p[r].x==R) return;
	PushDown(rt);
	int m=(l+r)>>1;
	if(L <=p[m].x) insert(L,R,v,ls);
	if(R > p[m].x) insert(L,R,v,rs); 
}
int Sn;
void FullSearch(int l,int r,int rt){ 
	if(l==Pn) return;
	if(l==r){
		if(!node[rt].v){
			s[Sn++].set(p[l].x,p[l+1].x);
		}
		return;
	}
	PushDown(rt);
	int m=(l+r)>>1;
	FullSearch(ls);
	FullSearch(rs);
}
int main(void)
{
	while(cin>>N){
		//建立所有的端点 
		Pn=3;
		map<int,int> MP;
		p[1].x=0;
		p[2].x=1000000000;
		FOR(i,N) {
			int a,b;char c;
			scanf("%d%d %c",&s[i].l,&s[i].r,&c);
			s[i].v=c=='b';
			int I;
			//用map来判重,去掉重复的端点。 
			if(MP.count(s[i].l)) I=MP[s[i].l];
			else MP[s[i].l]=Pn++,I=MP[s[i].l],p[I].x=s[i].l;
			if(MP.count(s[i].r)) I=MP[s[i].r];
			else MP[s[i].r]=Pn++,I=MP[s[i].r],p[I].x=s[i].r;
		}
		MP.clear();Pn--;
		//排序 
		sort(p+1,p+Pn+1);
		//建立线段树,初始化为白色 
		build(1,Pn,1);
		//进行区间上色 
		FOR(i,N) insert(s[i].l,s[i].r,s[i].v,1,Pn,1);//,OUT1(i),FullSearch(1,Pn,1);//OUT3(s[i].l,s[i].r,s[i].v);
		Sn=1;
		//搜索所有区间,将白色的存入 s数组 
		FullSearch(1,Pn,1);
		int Cur=1;
		//合并相邻的白色区间 
		for(int i=2;i<Sn;i++){
			if(s[Cur].r==s[i].l){
				s[Cur].r=s[i].r;
			}
			else{
				Cur++;
				s[Cur]=s[i];
			}
		}
		//搜索最长的白色区间。 
		Seg ANS=s[1];
		for(int i=2;i<=Cur;i++){
			if(s[i].r-s[i].l>ANS.r-ANS.l){
				ANS=s[i];
			}
		}
		//输出答案 
		printf("%d %d\n",ANS.l,ANS.r);
	}
return 0;
}





 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值