P1558 色板游戏

P1558 色板游戏

题目地址
思路:位运算,状态压缩,线段树

  1. 分析思路
    • 求某个区间的颜色集合的运算时支持 结合律的 ;
    • 看到 T ≤ 30 T≤30 T30 能想到 状态压缩
    • 所以可以设置状态: X \text{X} X的二进制下的第 i \text{i} i位为 1 \text{1} 1表示有第 i \text{i} i种颜色,否则没有 ;
    • 合并两个状态用到了按位或运算 ;
  2. 线段树
    • Pushup \text{Pushup} Pushup操作直接求或运算即可
    • 其他操作照常
  3. Code
    #include <stdio.h>
    #include <string.h>
    #define Clean(X) memset(X,0,sizeof(X))
    #define LS RT*2
    #define RS RT*2+1
    const int MaxL = 100005 , MaxK = 30;
    unsigned L , K , O ;
    inline unsigned QRead () {
    	unsigned X = 0;
    	char C = getchar() ;
    	while (C > '9' || C < '0') C = getchar() ;
    	while (C >='0' && C <='9') {
    		X = X * 10 + C -'0' ;
    		C = getchar() ;
    	}
    	return X ;
    }
    /*
    头文件和准备工作
    */
    unsigned Cnt(unsigned X) {
    	unsigned Ans = 0 ;
    	while (X) {
    		++Ans ;
    		X -= (X&(-X)) ;
    	}
    	return Ans ;
    }
    /*
    Cnt函数可以统计一个数的二进制位种所含有的1的个数。
    每次进行Lowbit运算,都消除了一个1和它后面的0。
    */
    unsigned Date[MaxL * 4] , Left[MaxL * 4] , Right [MaxL * 4] , Tag[MaxL * 4] ;
    void Plant (unsigned RT , unsigned L , unsigned R) {
    	Left[RT] = L , Right[RT] = R ;
    	Date[RT] = 1 ;
    	Tag[RT] = 0 ;
    	if (L == R) return  ;
    	unsigned Mid = (L + R) >> 1 ;
    	Plant (LS , L , Mid) , Plant (RS , Mid + 1 , R) ;
    }
    inline void Spd (unsigned RT) {
    	Tag[LS] = Tag[RS] = Date[LS] = Date[RS] = Tag[RT];
    	Tag[RT] = 0 ;
    }
    unsigned Ask (unsigned RT , unsigned L , unsigned R) {
    	if (Left[RT] >= L && Right[RT] <= R) return Date[RT] ;
    	unsigned Mid = (Left[RT] + Right[RT] ) >> 1 , Al = 0 , Ar = 0;
    	if (Tag[RT])Spd(RT) ;
    	if (L <= Mid) Al = Ask (LS , L , R) ;
    	if (R >  Mid) Ar = Ask (RS , L , R) ;
    	return Al | Ar ;
    }
    void Add (unsigned RT , unsigned L , unsigned R,unsigned K) {
    	if (Left[RT] >= L && Right[RT] <= R) {
    		Date[RT] = Tag[RT] = K ;
    		return  ;
    	}
    	unsigned Mid = (Left[RT] + Right[RT] ) >> 1 ;
    	if (Tag[RT] )Spd (RT) ;
    	if (L <= Mid) Add (LS , L , R , K) ;
    	if (R >  Mid) Add (RS , L , R , K) ;
    	Date[RT] = Date[LS] | Date[RS] ;
    }
    /*
    线段树的基本函数
    */
    void Swap(unsigned int &X, unsigned int &Y) {
    	int T = X ;
    	X = Y ;
    	Y = T ;
    }
    int main () {
    //	freopen ("P1558.in" , "r" , stdin) ;
    	L = QRead () , K = QRead () , O = QRead () ;
    	Plant (1 , 1 , L) ;
    	for (register unsigned i = 1 ;  i <= O ; ++ i) {
    		char C = getchar() ;
    		while (C != 'P' && C != 'C') C = getchar() ;
    		if (C == 'C') {
    			unsigned L = QRead () , R = QRead () , Cl = QRead () - 1 ;
    			if (L > R) Swap (L , R) ;
    			Add (1 , L , R , 1 << Cl) ;
    		} else {
    			unsigned L = QRead () , R = QRead () ;
    			if (L > R) Swap (L , R) ;
    			printf ("%d\n" , Cnt (Ask (1 , L , R)));
    		}
    	}
    	fclose (stdin) ;
    	fclose (stdout);
    	return 0;
    }
    
Thanks!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值