POJ 2777 Count Color

题目大意:

       现只有一个测例, 一个长度为L的长板,总共由L个相同的小段组成(1 ≤ L ≤ 100,000),小段从左到右编号为1 ~ L,现在给长板上色,规定每个小段只能涂一种颜色,刚开始时全部都涂成颜色1,假设总共有T中颜色(1 ≤ T ≤ 30),现在有O个操作(1 ≤ O ≤ 100,000),操作共有两种,一种是"C A B C",表示给编号区间[A, B]上颜色C(颜色用1 ~ T的整数表示),另一种操作是"P A B",表示要求你输出区间[A, B]之中不同颜色的总数。

题目链接

注释代码:

/*                       
 * Problem ID : POJ 2777 Count Color
 * Author     : Lirx.t.Una                       
 * Language   : C           
 * Run Time   : 250 ms                       
 * Run Memory : 840 KB                       
*/   

#include <stdio.h>

//板的最大长度
#define	MAXN		100000

#define	LFT(T)		( (T) << 1 )
#define	RHT(T)		( LFT(T) | 1 )

//将颜色数值转换成二进制表示方式
//颜色n就表示成二进制数第n位为1的形式
#define	COLOR(C)	( 1 << ( (C) - 1 ) )
//判断一个颜色是否为纯色,纯色就只有1位有1,混色多位有1
//即检查C的二进制数是否只含一个1
#define	SINGL(C)	( !( ( (C) - 1 ) & (C) ) )

//线段树,保存区间中颜色的二进制表示形式
//如果一个区间结点的颜色为0011,而其区间为[1, 5],
//则表示该区间中有两种颜色,一种是颜色1,另一种是颜色2
int		seg[MAXN * 3] = { 0, 1 };//根结点下标为1,因此将结点1区间都涂成颜色1

void
update( int tree, int c, int ul, int ur, int lft, int rht ) {
	//tree结点[lft, rht]中奖区间[ul, ur]颜色涂成二进制颜色c

	int		mid;
	int		tl, tr;//tree_lft and tree_rht
	int		tpc;//tmp_color,临时二进制颜色变量

	//上的颜色肯定是纯色的
	//如果当前结点区间已经是纯色而且和要上的色一样
	//那就相当于啥事儿都没干
	if ( c == seg[tree] ) return ;

	if ( lft == ul && ur == rht ) {//命中后区间直接上色,不呀再向下分解
	
		seg[tree] = c;
		return ;
	}

	tl  = LFT(tree);
	tr  = RHT(tree);
	mid = ( lft + rht ) >> 1;
	tpc = seg[tree];

	//未命中,因为命中的时候直接上色了,子结点不管情况如何都不管了,因此
	//纯色结点的子结点不一定能正确反映父结点的信息,所以必须往下带
	if ( SINGL(tpc) ) {//如果当前区间是纯色,需要将颜色往下带(分解)
	
		seg[tl] = tpc;
		seg[tr] = tpc;
	}

	if ( ur <= mid )
		update( tl, c, ul, ur, lft, mid );
	else if ( ul > mid )
		update( tr, c, ul, ur, mid + 1, rht );
	else {
	
		update( tl, c, ul, mid, lft, mid );
		update( tr, c, mid + 1, ur, mid + 1, rht );
	}

	//跟新父结点的颜色,就是两个子结点按位或,将所有颜色都集中
	seg[tree] = seg[tl] | seg[tr];
}

int
query( int tree, int ql, int qr, int lft, int rht ) {

	int		mid;

	//如果当前结点区间(大区间)[lft, rht]已经是纯色的了则直接return不必再继续分解
	//先检查纯色再检查是否命中可以省更多时间
	if ( SINGL( seg[tree] ) || lft == ql && qr == rht ) return seg[tree];

	mid = ( lft + rht ) >> 1;

	if ( qr <= mid ) return query( LFT(tree), ql, qr, lft, mid );
	else if ( ql > mid ) return query( RHT(tree), ql, qr, mid + 1, rht );
	else return query( LFT(tree), ql, mid, lft, mid ) |
				query( RHT(tree), mid + 1, qr, mid + 1, rht );
}

int
main() {

	int		n, t, q;//板长,颜色数,询问数
	int		lft, rht, c;//c接受临时颜色(1 ~ t之间的整数)

	int		tmp;//临时变量
	int		i;//计数变量
	char	cmd;

	int		ans;//询问区间的不同颜色数量

	scanf("%d%d%d", &n, &t, &q);

	while ( q-- ) {
	
		scanf("\n%c%d%d", &cmd, &lft, &rht);
		if ( lft > rht ) {//对于左端点大于右端点的交换端点
		
			tmp = lft;
			lft = rht;
			rht = tmp;
		}

		switch (cmd) {
			
			case 'C' :
			
				scanf("%d",&c);
				update( 1, COLOR(c), lft, rht, 1, n );
			
				break;
			
			case 'P' :
			
				ans = 0;
				c   = query( 1, lft, rht, 1, n );
			
				for ( i = 0; i < t; i++ )//统计二进制数中1的个数
					ans += 1 & ( c >> i );
				printf("%d\n", ans);
			
				break;
			
			default : break;
		}
	}
	
	return 0;
}
无注释代码:

#include <stdio.h>

#define	MAXN		100000

#define	LFT(T)		( (T) << 1 )
#define	RHT(T)		( LFT(T) | 1 )
#define	COLOR(C)	( 1 << ( (C) - 1 ) )
#define	SINGL(C)	( !( ( (C) - 1 ) & (C) ) )

int		seg[MAXN * 3] = { 0, 1 };

void
update( int tree, int c, int ul, int ur, int lft, int rht ) {

	int		mid;
	int		tl, tr;
	int		tpc;

	if ( c == seg[tree] ) return ;

	if ( lft == ul && ur == rht ) {
	
		seg[tree] = c;
		return ;
	}

	tl  = LFT(tree);
	tr  = RHT(tree);
	mid = ( lft + rht ) >> 1;
	tpc = seg[tree];

	if ( SINGL(tpc) ) {
	
		seg[tl] = tpc;
		seg[tr] = tpc;
	}

	if ( ur <= mid )
		update( tl, c, ul, ur, lft, mid );
	else if ( ul > mid )
		update( tr, c, ul, ur, mid + 1, rht );
	else {
	
		update( tl, c, ul, mid, lft, mid );
		update( tr, c, mid + 1, ur, mid + 1, rht );
	}

	seg[tree] = seg[tl] | seg[tr];
}

int
query( int tree, int ql, int qr, int lft, int rht ) {

	int		mid;

	if ( SINGL( seg[tree] ) || lft == ql && qr == rht ) return seg[tree];

	mid = ( lft + rht ) >> 1;

	if ( qr <= mid ) return query( LFT(tree), ql, qr, lft, mid );
	else if ( ql > mid ) return query( RHT(tree), ql, qr, mid + 1, rht );
	else return query( LFT(tree), ql, mid, lft, mid ) |
				query( RHT(tree), mid + 1, qr, mid + 1, rht );
}

int
main() {

	int		n, t, q;
	int		lft, rht, c;

	int		tmp;
	int		i;
	char	cmd;

	int		ans;

	scanf("%d%d%d", &n, &t, &q);

	while ( q-- ) {
	
		scanf("\n%c%d%d", &cmd, &lft, &rht);
		if ( lft > rht ) {
		
			tmp = lft;
			lft = rht;
			rht = tmp;
		}

		switch (cmd) {
		
			case 'C' :

				scanf("%d",&c);
				update( 1, COLOR(c), lft, rht, 1, n );

				break;

			case 'P' :

				ans = 0;
				c   = query( 1, lft, rht, 1, n );

				for ( i = 0; i < t; i++ )
					ans += 1 & ( c >> i );
				printf("%d\n", ans);

				break;

			default : break;
		}
	}

	return 0;
}
单词解释:

optional:adj, 可选的,选修的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值