2018年湘潭大学程序设计竞赛 H 统计颜色( 区间更新线段树 + 状态压缩 )

n个桶按顺序排列,我们用1~n给桶标号。有两种操作:
1 l r c 区间[l,r]中的每个桶中都放入一个颜色为c的球 (1≤l,r ≤n,l≤r,0≤c≤60)
2 l r   查询区间[l,r]的桶中有多少种不同颜色的球     (1≤l,r ≤n,l≤r)

输入描述:

有多组数据,对于每组数据:
第一行有两个整数n,m(1≤n,m≤100000)
接下来m行,代表m个操作,格式如题目所示。

输出描述:

对于每个2号操作,输出一个整数,表示查询的结果。
示例1

输入

10 10
1 1 2 0
1 3 4 1
2 1 4
1 5 6 2
2 1 6
1 7 8 1
2 3 8
1 8 10 3
2 1 10
2 3 8

输出

2
3
2
4
3

思路:刚看到这道题就是想到了线段树,但是,难道要 在结构体中开一个60的数组吗,答案是否定,这样不是超时,就是 内存超限,但是是就没有不能就线段树写了吗? 当然不是,这时候我想到了状态压缩,不是有 60 中颜色吗, long long 有64,每一位代表当前颜色是不是不是存在; 还有就是 在用 左移 右移 运算符时 1<<val, 一定要强制转化为  long long ((ll)1<<val) 

因为 << 运算符默认的是 int 型;

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 100010
#define ll long long 
struct node
{
	ll x;
	ll sum;
	int downnode;   // 延迟标记,(也可以吧x当做延迟标记,往下传时那就得把x清0)
// 但是不把x当做延迟标记的话,那么就把x清不清0,就不影响了,因为是二进制符号|; 
} stu[Max*4];
int n,m;

void down(int root)
{
		stu[2*root].x |= stu[root].x;
		stu[2*root+1].x |= stu[root].x;
		stu[2*root].sum |= stu[root].x;
		stu[2*root+1].sum |= stu[root].x;
		stu[2*root].downnode = 1;
		stu[2*root+1].downnode = 1;
		stu[root].downnode = 0;
}
void build(int root,int star,int end)
{
	stu[root].x = 0;
	stu[root].sum = 0;
	stu[root].downnode = 0; 
	if(star==end)
		return ;
	int mid = (star+end)/2;
	build(2*root,star,mid);
	build(2*root+1,mid+1,end);
}

void updet(int root,int star,int end,int l,int r,int val)
{
	if(star>r||end<l)
		return ;
	if(star>=l&&r>=end)
	{
		stu[root].downnode = 1;
		stu[root].x |= (ll)1<<val;     // 一定要强制转化为 long long  
		stu[root].sum |= (ll)1<<val;
		return ;
	}
	if(stu[root].downnode)
	{
		down(root);
	}
	int mid = (star + end)/2;
	if(l<=mid)
		updet(root*2,star,mid,l,r,val);
	if(r>mid) 
		updet(root*2+1,mid+1,end,l,r,val);
	stu[root].sum = stu[root*2].sum | stu[root*2+1].sum;
}
ll query(int root,int star,int end,int l,int r)
{
	if(star>r||end<l)
		return 0;
	if(star>=l&&end<=r)
		return stu[root].sum;
	if(stu[root].downnode)
		down(root);
	int mid = (star+end)/2;
	ll t1 = 0,t2 = 0;
	if(l<=mid)
		t1 = query(root*2,star,mid,l,r);
	if(r>mid)
		t2 = query(root*2+1,mid+1,end,l,r);
	return t1|t2;
}

int main()
{
	int i,j,k;
	while(~scanf("%d%d",&n,&m))
	{
		build(1,1,n);
		//memset(stu,0,sizeof(stu));
		int t;
		for(i = 0;i<m;i++)
		{
			scanf("%d",&t);
			int l,r,val;
			if(t==1)
			{
				scanf("%d%d%d",&l,&r,&val);
				updet(1,1,n,l,r,val);
			}
			else if(t==2)
			{
				scanf("%d%d",&l,&r);
				ll t = query(1,1,n,l,r);
				int sum = 0;
				while(t)
				{
					if(t%2)
						sum++;
					t=t/2;
				}
				printf("%d\n",sum);
			}
		}
	}
	return 0;
}

方法二:

其实这道题有简单的方法,定义一个 结构体里面是区间 和 vecor,里面 v[i] 中存的是 颜色改变为 i 的 区间;每查询一次区间,就遍历这60种颜色,看看中间有没相交的部分,有的话,就把这种颜色算上,没有就不算;

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 100010
#include<vector>

struct node
{
	int l,r;
};
int n,m;
vector<node> v[61];
int main()
{
	int i,j;
	while(~scanf("%d%d",&n,&m))
	{
		for(i = 0;i<=60;i++)
			v[i].clear();
		int k;
		int l,r,val;
		for(i = 0;i<m;i++)
		{
			scanf("%d",&k);
			if(k==1)
			{
				scanf("%d%d%d",&l,&r,&val);
				node tt;
				tt.l = l;
				tt.r = r;
				v[val].push_back(tt);
			}
			else if(k==2)
			{
				scanf("%d%d",&l,&r);
				int sum =0,t;
				for(t = 0;t<=60;t++)
				{
					for(j = 0;j<v[t].size();j ++)
					{
						node tt = v[t][j];
						if(tt.r<l||tt.l>r)
							continue;
						else
						{
							sum++;
							break;
						}
					}
				}
				printf("%d\n",sum);
			}
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值