Poj 2777 Count Color(可合并区间线段树)

题目链接
https://vjudge.z180.cn/problem/POJ-2777
题目大意:
给你一个长度为N的 [ 1 , N ] [1,N] [1,N]区间,一开始区间的颜色初始化为1,给你一个T表示颜色在[1,T]范围类可选,然后你有M次操作,操作有如下两种情况
1.C A B C 表示把区间 [ A , B ] [A,B] [A,B]染成颜色C
2.P A B表示查询区间 [ A , B ] [A,B] [A,B]有多少种不同颜色,并输出

分析:这道题一看区间修改加区间查询,线段树跑不掉,首先对于一个区间我们不能单纯通过它左儿子不同颜色个数+右儿子不同颜色个数来维护这个区间的不同颜色个数,你想一想如果左儿子也有红色,右儿子也有红色,那么不就把红色重复记录了吗?所以我们不防把颜色状态用二进制表示,然后用|操作来跟新,举个例子,[1,4]区间的颜色分别是[1,2,2,6],那么它左儿子区间的颜色状态为[1,2]用二进制表示就是011(1和2位有1,代表有颜色1和颜色2),它右儿子区间的颜色状态为[5,6]用二进制表示为00100010,那么区间[1,4]的颜色状态就是011|00100010==00100011,这时候2号颜色就没有被重复记录,是不是很巧妙?,然后我们通过线段树去维护区间颜色状态的二进制就可以了.

AC代码:

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ls dep<<1
#define rs dep<<1|1
using namespace std;
const int Maxn = 1e5+10;
int mask[Maxn*4];
int tag[Maxn*4];
int get_number(int x)
{
	int ans = 0;
	while(x)
	{
		ans++;
		x&=(x-1);
	}
	return ans;
}
void pushdown(int dep)
{
	if(tag[dep]>=0)
	{
	mask[ls] = mask[rs] = 1<<tag[dep];
	tag[ls] = tag[rs] = tag[dep];
	tag[dep] = -1;
	return ;
    }
}
void pushup(int dep)
{
	mask[dep]=(mask[ls]|mask[rs]);
	return ;
}
void update(int ql,int qr,int dep,int val,int l,int r)
{
	if(ql<=l&&r<=qr)
	{
		mask[dep]  = 1<<val;
		tag[dep] = val;
		return ;
	}
	pushdown(dep);
	int mid = l+r>>1;
	if(ql<=mid) update(ql,qr,ls,val,l,mid);
	if(qr>mid) update(ql,qr,rs,val,mid+1,r);
	pushup(dep);
	return ;
}
int query(int ql,int qr,int dep,int l,int r)
{
	if(ql<=l&&r<=qr)
	{
		return mask[dep];
	}
	pushdown(dep);
	int mid = l+r>>1;
	int ret = 0;
	if(ql<=mid) ret|=query(ql,qr,ls,l,mid);
	if(qr>mid)  ret|=query(ql,qr,rs,mid+1,r);
	return ret;
}
void build(int pos,int dep,int l,int r)
{
	if(l==r)
	{
		mask[dep] = 1;
		return ;
	}
	int mid = l+r>>1;
	if(pos<=mid) build(pos,ls,l,mid);
	else build(pos,rs,mid+1,r);
	pushup(dep);
	return ;
}
int main()
{
	for(int i=1;i<Maxn*4;i++) mask[i] = 1;
	memset(tag,-1,sizeof(tag));
	int n,T,m;
	cin>>n>>T>>m;
	/*for(int i=1;i<=n;i++)
	{
		build(i,1,1,n);
	}*/
	for(int i=1;i<=m;i++)
	{
		char s[5];
		scanf("%s",s);
		if(s[0]=='C')
		{
			int A,B,C;
			cin>>A>>B>>C;
			update(min(A,B),max(A,B),1,C-1,1,n);
		}
		else
		{ 
			int A,B;
			cin>>A>>B;
			int ans = get_number(query(min(A,B),max(A,B),1,1,n));
			cout<<ans<<'\n';
		}
	}
	return 0;
}

如果还是不太懂这里有篇视频讲的很清楚
视频讲解链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值