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 
    #include 
    #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!

你可能感兴趣的:(线段树)