poj2777

12 篇文章 0 订阅
6 篇文章 0 订阅

题目不难,和poj2528相似,都是求区间最终可以看见的颜色个数。

题目大意为:给定n段,分别编号为1、2、……、n。初始化时都涂色为1,现在规定两中操作:

1)C A B C,给编号为A和B段涂色C

2)P A B,输出编号A到B段的涂色数

现在给定T为颜色的种类数(1——T)和O为操作总数,要求按照相应的操作输出P A B。

分析如下: 令线段树节点定义为: l,r,value,flag分被表示区间左右端点,该区间的涂色为value,flag为标记量,true表示该区间涂色为最新的涂色,否则表示该区间的子区间有更新的涂色。由于初始时均涂色为1,故value的值均为1,flag均为true(即都是最新的涂色)。本题的难点在于更新和查找。

线段树的更新:寻找输入的涂色区域,在寻找的过程中若发现节点的flag为true,则要先更新子节点的value和flag然后更新自身的value和flag(注意顺序不能反了),当查找到区间时,直接赋值value和更新flag为true。

线段树的查找:线段树的更新和查找是联系起来的,这里主要说明一下查找的技巧:

朴素的查找就是边查找边更新,直到找到与查找路径相同的区间为止,其实这样得不偿失,消费了许多时间在无用的更新操作上,也使查找深度加深,所以可以只要flag为true,而不管该线段树区间时等于查找区间还是大于查找区间,由于是最新的涂色而且必定包含查找区间,故可以直接判断该涂色,并返回。这是查找关键,没有这一步就等着TLE。

下面是代码:4268K+329MS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 40
#define Maxx 100010
#define Lson(p) (p<<1)
#define Rson(p) (p<<1|1)
#define Mid(a,b) ((a+b)>>1)
#define Minn(a,b) ((a)<(b))?(a):(b)
#define MAXX(a,b) ((a)>(b))?(a):(b)
struct Node{
	int l,r,value;
	bool flag;
}node[Maxx*3];
bool trag[Max]; //每次查找时标记颜色是否已经存在
int L,T,O;
int ans; //最终结果
void build_tree(int left,int right,int po){ //建树,初始化value为1,flag为true,即都是最新的涂色1
	node[po].l=left,node[po].r=right,node[po].value=1,node[po].flag=true;
	if(left==right)
		return ;
	int mid=Mid(left,right);
	build_tree(left,mid,Lson(po));
	build_tree(mid+1,right,Rson(po));
}
void update_tree(int left,int right,int v,int po){ //更新线段树
	if(left==node[po].l && right==node[po].r){ //若找到区间则赋值value为最新颜色,并置flag为true
		//printf("color:%d %d\n",left,right);
		node[po].value=v;
		node[po].flag=true;
		return ;
	}
	if(node[po].flag){ // 否则,更新路径上的‘次’最新涂色节点,先儿子后父节点
		node[Lson(po)].value=node[po].value;
		node[Rson(po)].value=node[po].value;
		node[po].flag=false; //置标记为false,表示不是最新涂色了
	    node[Lson(po)].flag=true;  //表示是最新涂色
		node[Rson(po)].flag=true;
	}
	int mid=Mid(node[po].l,node[po].r); 
	if(right<=mid) //向下搜索
		update_tree(left,right,v,Lson(po));
	else if(left>=mid+1)
		update_tree(left,right,v,Rson(po));
	else{
		update_tree(left,mid,v,Lson(po));
		update_tree(mid+1,right,v,Rson(po));
	}
}
void search_tree(int left,int right,int po){ //查找线段树,其中没有注释的是优化后的查询,加了注释的是朴素查询(TLE)
	if(/*left==node[po].l && right==node[po].r &&*/ node[po].flag){ //只要区间包含即可判断查找区间颜色
		//printf("search:%d %d %d\n",left,right,node[po].value); 
		if(!trag[node[po].value]){
			trag[node[po].value]=true;
			ans++;
		}
		return ;
	}
	/*if(node[po].flag){ //多余更新操作
		node[Lson(po)].value=node[po].value;
		node[Rson(po)].value=node[po].value;
		node[po].flag=false;
	    node[Lson(po)].flag=true;
		node[Rson(po)].flag=true;
	}*/
	int mid=Mid(node[po].l,node[po].r);
	if(right<=mid) //向下搜索
		search_tree(left,right,Lson(po));
	else if(left>=mid+1)
		search_tree(left,right,Rson(po));
	else{
		search_tree(left,mid,Lson(po));
		search_tree(mid+1,right,Rson(po));
	}
}
	
int main(){
	while(scanf("%d%d%d",&L,&T,&O)!=EOF){
	build_tree(1,L,1); //建树
	getchar();
	int a,b,v;
	for(int i=0;i<O;i++){
		char temp=getchar();
		if(temp=='C'){ //更新线段树
			scanf("%d%d%d",&a,&b,&v);
			update_tree(Minn(a,b),MAXX(a,b),v,1);
		}
		else{ //查找
			scanf("%d%d",&a,&b);
			memset(trag,0,sizeof(trag)); //每次都初始化为未出现
			ans=0; //计数结果初始化为0
			search_tree(Minn(a,b),MAXX(a,b),1);
			printf("%d\n",ans);
		}
		getchar();
	}
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值