【FZU】2105 Digits Count 线段树

2024-09-05 14:48
文章标签 线段 count digits fzu 2105

本文主要是介绍【FZU】2105 Digits Count 线段树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

传送门:【FZU】2105 Digits Count


题目分析:与、或、异或三种操作都是每一位相互独立的,所以可以将线段树建四棵,分别对应一位,and和or操作相当于覆盖操作,xor操作相当于反转操作,就和普通的线段树方法类似,设立两个lazy标记即可。查询的时候求得每一位的1的个数*权重(1,2,4,8),全部累加就是答案。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <map>
using namespace std ;typedef long long LL ;#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define clr( a , x ) memset ( a , x , sizeof a )
#define CPY( a , x ) memcpy ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define rt o , l , r
#define root 1 , 0 , n - 1
#define mid ( ( l + r ) >> 1 )const int MAXN = 1000005 ;int Sum[4][MAXN << 2] ;
int Xor[4][MAXN << 2] ;
int Set[4][MAXN << 2] ;int n , m ;void pushup ( int o ) {rep ( i , 0 , 4 ) Sum[i][o] = Sum[i][ls] + Sum[i][rs] ;
}void pushdown ( int o , int l , int r ) {int m = mid ;rep ( i , 0 , 4 ) {if ( ~Set[i][o] ) {Set[i][ls] = Set[i][rs] = Set[i][o] ;Sum[i][ls] = Set[i][o] ? m - l + 1 : 0 ;Sum[i][rs] = Set[i][o] ? r - m : 0 ;Xor[i][ls] = Xor[i][rs] = 0 ;Set[i][o] = -1 ;}if ( Xor[i][o] ) {Sum[i][ls] = m - l + 1 - Sum[i][ls] ;Sum[i][rs] = r - m - Sum[i][rs] ;Xor[i][ls] ^= 1 ;Xor[i][rs] ^= 1 ;Xor[i][o] = 0 ;}}
}void build ( int o , int l , int r ) {rep ( i , 0 , 4 ) Set[i][o] = -1 , Xor[i][o] = 0 ;if ( l == r ) {int x ;scanf ( "%d" , &x ) ;rep ( i , 0 , 4 ) {Sum[i][o] = ( ( x >> i ) & 1 ) ;//printf ( ":%d" , x & ( 1 << i ) ) ;}//printf ( "\n" ) ;return ;}int m = mid ;build ( lson ) ;build ( rson ) ;pushup ( o ) ;
}void update ( int L , int R , int op , int v , int o , int l , int r ) {if ( L <= l && r <= R ) {if ( op == 0 ) {rep ( i , 0 , 4 ) {int tmp = ( ( v >> i ) & 1 ) ;if ( tmp == 0 ) {Sum[i][o] = 0 ;Set[i][o] = 0 ;Xor[i][o] = 0 ;}}} else if ( op == 1 ) {rep ( i , 0 , 4 ) {int tmp = ( ( v >> i ) & 1 ) ;if ( tmp ) {Sum[i][o] = r - l + 1 - Sum[i][o] ;Xor[i][o] ^= tmp ;}}} else {rep ( i , 0 , 4 ) {int tmp = ( ( v >> i ) & 1 ) ;if ( tmp == 1 ) {Sum[i][o] = r - l + 1 ;Set[i][o] = 1 ;Xor[i][o] = 0 ;}}}return ;}int m = mid ;pushdown ( rt ) ;if ( L <= m ) update ( L , R , op , v , lson ) ;if ( m <  R ) update ( L , R , op , v , rson ) ;pushup ( o ) ;
}int query ( int L , int R , int i , int o , int l , int r ) {if ( L <= l && r <= R ) return Sum[i][o] ;int m = mid ;pushdown ( rt ) ;if ( R <= m ) return query ( L , R , i , lson ) ;if ( m <  L ) return query ( L , R , i , rson ) ;return query ( L , R , i , lson ) + query ( L , R , i , rson ) ;
}void solve () {char buf[10] ;int v , L , R ;scanf ( "%d%d" , &n , &m ) ;build ( root ) ;while ( m -- ) {scanf ( "%s" , buf ) ;if ( buf[0] == 'A' ) {scanf ( "%d%d%d" , &v , &L , &R ) ;update ( L , R , 0 , v , root ) ;} else if ( buf[0] == 'X' ) {scanf ( "%d%d%d" , &v , &L , &R ) ;update ( L , R , 1 , v , root ) ;} else if ( buf[0] == 'O' ) {scanf ( "%d%d%d" , &v , &L , &R ) ;update ( L , R , 2 , v , root ) ;} else {scanf ( "%d%d" , &L , &R ) ;int x = 0 ;rep ( i , 0 , 4 ) {x += ( 1 << i ) * query ( L , R , i , root ) ;//printf ( "debug --%d\n" , x ) ;}printf ( "%d\n" , x ) ;}}
}int main () {int T ;scanf ( "%d" , &T ) ;while ( T -- ) solve () ;return 0 ;
}



这篇关于【FZU】2105 Digits Count 线段树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139267

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

圆与线段的交点

poj 3819  给出一条线段的两个端点,再给出n个圆,求出这条线段被所有圆覆盖的部分占了整条线段的百分比。 圆与线段的交点 : 向量AB 的参数方程  P = A + t * (B - A)      0<=t<=1 ; 将点带入圆的方程即可。  注意: 有交点 0 <= t <= 1 ; 此题求覆盖的部分。 则 若求得 t  满足 ; double ask(d