线段树染色问题(例题为poj2777)

南笙酒味 提交于 2020-01-30 00:06:26

染色问题加离散化是poj2528,过后我会放出来的


关于离散的详细解释参考博客:https://blog.csdn.net/iwts_24/article/details/81603603


区域染色覆盖问题

        假设某大学有一面文化墙,各个学院都可以在上面涂色,要求涂色区域高必须和墙一样,宽度任意但必须是整数(以米为单位)。涂色可以覆盖其他学院的涂色。现在若干个学院涂色之后最终这面墙上能看见多少种颜色。

        这就是简单的染色问题了。当然有很多种不同的说法,但整体上离不开这两个问题:1.区域覆盖。2.多种染色方式。既然是区域操作,那么用线段树是比较合理了。用数组也可以,但是数据规模稍微大一点就会TLE。

        当然,跟染色问题一同存在的还有就是离散化,很多博客也是将这两者一起来写的,并且主要是离散化。但我个人感觉染色问题对理解线段树很有帮助(当然还是太菜了= =大佬们都认为难点是线段树的离散化),所以就单独拿出来了。

建树

        线段树有多种表现形式,感觉很多人都是拿结构体写的,博主是用数组写的,其本质还是一样的代码有些不一样而已。

        整体上,我们对一段数据建成线段树,那么区域染色的时候就类似与修改区间。而线段树的精髓就是利用lazy数组,可以保证不遍历到子结点就可以获得区域的情况。那么染色也要利用这个性质,对于一段区间,我们想要在上层结点上来表示出来。那么我们可以设置3个变量:-1 表示当前区段有多种颜色,具体有多少种不用管。0表示当前区域未染色。正整数表示当前区域染了单一染色,并且颜色号是这个正整数。

        所以除了特殊要求,一般我们用int来表示颜色种类(就算题目是char类型或者string,我们也可以变成int,最多写一个hash,还是整数方便)。那么看下面这个例子:
————————————————


 这是按照上面的要求写的线段树,-1表示当前段有多种颜色,1表示当前段全部是1。这里左边,一个1一个0,而上面结点仍然是-1。这是因为默认把0也当成一种颜色了,可以节省代码,实际在查找的时候我们只用排除0就可以得到正确答案。

        总之,这样建立的线段树可以表示区域染色的大致情况。而具体有多少种颜色需要在查询的时候进行操作。
 

然后就是对线段树模板的改造了 每个地方改造在每个子函数里 这道题对我来说最大的收获就是知道怎么去染色...因为接下来离散化那题我刚开始是没有染色的想法所以都不知道怎么建树ORZ

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
const long int maxn=2e5+10;
int a[maxn*4];
int tree[maxn*4];
int color[40];
int lazy[maxn*4];
int n,t,colors;
int ans;
//0 表示没染色,-1表示杂,正整数表示该种的纯色  //本题目要求刚开始全为1 

void Pushup(int rt)
{
	if(tree[rt*2]==tree[rt*2+1])
	{
		tree[rt]=tree[rt*2];		//两个子叶颜色一样说明可能是纯的或者都是-1 
	}
	else
	tree[rt]=-1;  ///-1表示杂的颜色 
}

void Build(int l,int r,int rt)
{
	if(l==r)
	{
		tree[rt]=a[l];return;
	}
	
	int m=(l+r)/2;
	Build(l,m,rt*2);
	Build(m+1,r,rt*2+1);
	
	Pushup(rt);
}

void Pushdown(int rt,int llen,int rlen)  ///这道题不是长度了 
{
	if(lazy[rt]!=0)
	{
		lazy[rt*2]=lazy[rt];
		lazy[rt*2+1]=lazy[rt];
		
		tree[rt*2]=lazy[rt];
		tree[rt*2+1]=lazy[rt];
		
		lazy[rt]=0;
		
	}
}

void Update(int l,int r,int rt,int L,int R,int C)
{
	if(L<=l&&r<=R)
	{
	    tree[rt]=C;
		lazy[rt]=C;
		return;
	}
	int m=(l+r)/2;
	
	Pushdown(rt,m-l+1,r-m);	
	
	if(L<=m) Update(l,m,rt*2,L,R,C);        ////死循环一定是哪里小地方写错了 
	if(m<R)	 Update(m+1,r,rt*2+1,L,R,C);
	Pushup(rt);
	
}
void Query(int l,int r,int rt,int L,int R)
{
	
	if(L<=l&&r<=R)
	  {
		if(tree[rt]==0) return;
		
		else if(tree[rt]==-1)  //杂色继续往下找 
		{
			int m=(l+r)/2;
			///往下走要更新lazy			
			Pushdown(rt,m-l+1,r-m);
			
			if(L<=m)	Query(l,m,rt*2,L,R);
			
			if(m<R)		Query(m+1,r,rt*2+1,L,R);	
		}		
		
		else		//代表正整数的情况 
		{
			if(color[tree[rt]]==0) //该种纯色未被访问过 
			{
				ans++;             ///加一次 
				color[tree[rt]]=1;  ///该种纯色不再加 
			}
		}
	return;			
	}	
	int m=(l+r)/2;
	
	Pushdown(rt,m-l+1,r-m);
	if(L<=m) Query(l,m,rt*2,L,R);
	if(m<R)	 Query(m+1,r,rt*2+1,L,R);		

}
int main(void)
{
	while(scanf("%d%d%d", &n, &colors, &t)!=EOF)
	{
		memset(lazy,0,sizeof(lazy));memset(tree,0,sizeof(tree));
		
	
		int g,h,j;
		for(int i=1;i<=n+1;i++)
		 	a[i]=1;
		 	
		Build(1,n,1);
	
		for(int i=1;i<=t;i++)
		{
			ans=0;memset(color,0,sizeof(color));
			char str[3];
			scanf("%s", str);
			
			int t;
			if(str[0]=='C')
			{
				
				scanf("%d%d%d",&g,&h,&j);
				if(g>h)
				{
					t=g;g=h;h=t;	
				}
				Update(1,n,1,g,h,j);	
			}		
			else if(str[0]=='P')
			{
		        scanf("%d%d",&g,&h);
				if(g>h)
				{
					t=g;g=h;h=t;		
				}
				Query(1,n,1,g,h);		
				printf("%d\n",ans);memset(color,0,sizeof(color));		
		    } 
	    } 	
	}
return 0;	
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!