线段树

题目链接:https://www.nowcoder.com/acm/contest/105/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

基本的线段树应用,区间更新,区间求和。

重点在于在线跟新时lazy_tag的处理,对更新的更新需要累加,而不是直接把父节点的值复制过来。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

#define MAXN 100005
#define INF 100

using namespace std;

long long seg[MAXN<<2]; // seg[i]64位 每一位代表一种颜色 
long long flg[MAXN<<2];
int seg_n;

void init( int _n ) {
	seg_n = 1;
	memset( seg, 0, sizeof(seg) ); 
	memset( flg, 0, sizeof(flg) );
	while ( seg_n < _n ) seg_n<<=1;
} 

// 强烈要求注意这里的lazy_tag更新方式
// 在这里错了无数发提交 
// 将父节点的更新值 累加到当前节点 而不是简单的复制过来 
void pushdown( int l, int r, int k ) {
	if ( r-l == 1 ) return ;
	flg[2*k+1] |= flg[k];
	seg[2*k+1] |= flg[k];
	
	flg[2*k+2] |= flg[k];
	seg[2*k+2] |= flg[k];
	flg[k] = 0; 
}

void pushup( int k ) {
	seg[k] = seg[2*k+1] | seg[2*k+2];
}

void update( int l, int r, int a, int b, int k, int c ) {
	if ( l >= b || r <= a ) return;
	if ( a <= l && b >= r ) {
		// 当心数字溢出 
		seg[k] |= (1LL<<c);
		flg[k] |= (1LL<<c);
		return ; 
	}
	if ( flg[k] ) pushdown(l, r, k);
	update( l, (l+r)>>1, a, b, 2*k+1, c );
	update( (l+r)>>1, r, a, b, 2*k+2, c );
	pushup(k);
} 

long long query( int l, int r, int a, int b, int k ) {
	if ( l >= b || r <= a ) return 0;
	if ( a <= l && b >= r ) return seg[k];
	if ( flg[k] ) pushdown(l, r, k);
	return query(l, (l+r)>>1, a, b, 2*k+1) | query((l+r)>>1, r, a, b, 2*k+2); 
}

int tran( long long ans ) {
	int res = 0;
	while ( ans > 0 ) {
		res ++;
		ans &= ans-1;
	}
	return res;
}

int main()
{
	int n, m;
	int op, l, r, c;
	long long ans;
	while ( scanf( "%d%d", &n, &m ) != EOF ) {
		init( n );
		for ( int i = 0 ; i < m ; ++ i ) {
			scanf( "%d", &op );
			if ( op == 1 ) {
				scanf( "%d%d%d", &l, &r, &c );
				update( 0, seg_n, l-1, r, 0, c );
			} else {
				scanf( "%d%d", &l, &r );
				ans = query( 0, seg_n, l-1, r, 0 );
				printf( "%d\n", tran(ans) );
			}
		}
	}
	
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值