ACM: FZU 2105 Digits Count - 位运算的线段树【黑科技福利】
Time Limit:10000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u
Description
Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations:
Operation 1: AND opn L R
Here opn, L and R are integers.
For L≤i≤R, we do A[i]=A[i] AND opn (here "AND" is bitwise operation).
Operation 2: OR opn L R
Here opn, L and R are integers.
For L≤i≤R, we do A[i]=A[i] OR opn (here "OR" is bitwise operation).
Operation 3: XOR opn L R
Here opn, L and R are integers.
For L≤i≤R, we do A[i]=A[i] XOR opn (here "XOR" is bitwise operation).
Operation 4: SUM L R
We want to know the result of A[L]+A[L+1]+...+A[R].
Now can you solve this easy problem?
Input
The first line of the input contains an integer T, indicating the number of test cases. (T≤100)
Then T cases, for any case, the first line has two integers n and m (1≤n≤1,000,000, 1≤m≤100,000), indicating the number of elements in A and the number of operations.
Then one line follows n integers A[0], A[1], ..., A[n-1] (0≤A[i]<16,0≤i<n).
Then m lines, each line must be one of the 4 operations above. (0≤opn≤15)
Output
Sample Input
- 1
- 4 4
- 1 2 4 7
- SUM 0 2
- XOR 5 0 0
- OR 6 0 3
- SUM 0 2
Sample Output
- 7
- 18
Hint
A = [1 2 4 7]
SUM 0 2, result=1+2+4=7;
XOR 5 0 0, A=[4 2 4 7];
OR 6 0 3, A=[6 6 6 7];
SUM 0 2, result=6+6+6=18.
- /*/
- 题意:
- 给出一组数,然后有4种操作。
- AND opn l r 对 l~r 段的数与 opn 进行&运算;
- OR opn l r 对 l~r 段的数与 opn 进行|运算;
- XOR opn l r 对 l~r 段的数与 opn 进行^运算;
- SUMl r 对 l~r 段的数求和,并输出。
- 很明显的线段树,可是我还是太年轻。一开始以为只是一棵裸树,结果写到一半,发现不能对求和的数再进行与或非的运算,也不知道我哪里来的勇气,想到,既然不能对和去运算,不如把lazy全压下去。。。MDZZ。。。
- 后来,队友提示我可以用二进制来存数,然后与或非的情况也就变得特别简单了。
- 然后就用关于二进制的线段树来写了这个,思路一开始是很混乱的,不过写到后面还是被 >> 和 << 坑了好久,还是修行不精啊。。。
- A了但是运行时间还是比较久。
- 最后集训队队长发了个福利,读入优化,速度爆炸了,又是我的代码运行速度第一(233333)。
- AC代码:
- /*/
- #include"algorithm"
- #include"iostream"
- #include"cstring"
- #include"cstdlib"
- #include"cstdio"
- #include"string"
- #include"vector"
- #include"stack"
- #include"queue"
- #include"cmath"
- #include"map"
- using namespace std;
- typedef long long LL ;
- #define lson l,m,rt<<1
- #define rson m+1,r,rt<<1|1
- #define FK(x) cout<<"["<<x<<"]\n"
- #define memset(x,y) memset(x,y,sizeof(x))
- #define memcpy(x,y) memcpy(x,y,sizeof(x))
- #define smallfor(T) for(int i=0 ;i<T ;i++)
- #define bigfor(T) for(int qq=1;qq<= T ;qq++)
- const int MX =1111111;
- const int INF=0x3f3f3f3f;
- int sum[MX<<2][4],lazy[MX<<2][4];
- void PushUp(int rt,int i) {
- sum[rt][i]=sum[rt<<1][i]+sum[rt<<1|1][i];
- }
- void PushDown(int rt,int m,int i) {
- if(lazy[rt][i]==0) { //如果进行了AND操作,并且该位为0 清空下面子树。
- lazy[rt<<1][i]=0;
- lazy[rt<<1|1][i]=0;
- sum[rt<<1][i]=sum[rt<<1|1][i]=0;
- }
- if(lazy[rt][i]==1) { //如果进行了OR 操作,并且该位为1 填满下面子树。
- lazy[rt<<1][i]=1;
- lazy[rt<<1|1][i]=1;
- sum[rt<<1][i]=m-(m>>1);
- sum[rt<<1|1][i]=m>>1;
- }
- if(lazy[rt][i]==2) { //如果进行了XOR操作
- if(lazy[rt<<1][i]==INF) { //如果没有进行过任何操作,标记为XOR操作
- lazy[rt<<1][i]=2;
- sum[rt<<1][i]=m-(m>>1)-sum[rt<<1][i];
- } else if(lazy[rt<<1][i]==2) { //如果进行过XOR操作,a^b^b==a 恢复操作内容。
- lazy[rt<<1][i]=INF;
- sum[rt<<1][i]=m-(m>>1)-sum[rt<<1][i];
- } else { //如果进行了操作并且不是XOR操作 将该操作再取XOR操作
- lazy[rt<<1][i]^=1;
- if(lazy[rt<<1][i]==0) sum[rt<<1][i]=0;
- else sum[rt<<1][i]=m-(m>>1);
- }
- // 另一棵子树用同样的方法处理
- if(lazy[rt<<1|1][i]==INF) {
- lazy[rt<<1|1][i]=2;
- sum[rt<<1|1][i]=(m>>1)-sum[rt<<1|1][i];
- } else if(lazy[rt<<1|1][i]==2) {
- lazy[rt<<1|1][i]=INF;
- sum[rt<<1|1][i]=(m>>1)-sum[rt<<1|1][i];
- } else {
- lazy[rt<<1|1][i]^=1;
- if(lazy[rt<<1|1][i]==0) sum[rt<<1|1][i]=0;
- else sum[rt<<1|1][i]=(m>>1);
- }
- }
- lazy[rt][i]=INF; //标记lazy为空
- }
- void Build(int l,int r,int rt) {
- for(int i=0; i<4; i++) lazy[rt][i]=INF; //清空懒惰标记
- if(r==l) {
- int temp;
- scanf("%d",&temp);
- // FK("temp=="<<temp);
- for(int i=0; i<4; i++) {
- sum[rt][i]=(bool)(temp&(1<<i));//【这里一定要取(bool)否则得到的值不会是1,而是比 1大的数】
- // 该题目的方法是用sum保存每个位上值的总数,再改变为10进制,求和。
- // 把数按照二进制保存在4个位上面
- // FK(sum[rt][i]);
- }
- return;
- }
- int m=(r+l)>>1;
- Build(lson);
- Build(rson);
- for(int i=0; i<4; i++) PushUp(rt,i);
- }
- void UpData(int L,int R,int v,int i,int l,int r,int rt) {
- if(r<=R&&L<=l) {
- switch(v) {
- case 0:
- sum[rt][i]=0,lazy[rt][i]=v;
- //如果是进行AND操作,并且是0,清空和。
- break;
- case 1:
- sum[rt][i]=r-l+1,lazy[rt][i]=v;
- //如果是进行OR 操作,并且是1,填满和。
- break;
- case 2:
- sum[rt][i]=r-l+1-sum[rt][i];
- if(lazy[rt][i]==2) lazy[rt][i]=INF;
- else if(lazy[rt][i]==INF) lazy[rt][i]=2;
- else lazy[rt][i]^=1;
- break;
- default:
- break;
- }
- return ;
- }
- PushDown(rt,r-l+1,i);
- int m=(r+l)>>1;
- if(L<=m)UpData(L,R,v,i,lson);
- if(R>m) UpData(L,R,v,i,rson);
- PushUp(rt,i);
- }
- int Query(int L,int R,int i,int l,int r,int rt) {
- if(L<=l&&r<=R) {
- return sum[rt][i];
- // 返回这个数该位的和。
- }
- int m=(r+l)>>1;
- int sum=0;
- PushDown(rt,r-l+1,i);
- if(L<=m)sum+=Query(L,R,i,lson);
- if(R>m) sum+=Query(L,R,i,rson);
- return sum;
- }
- int main() {
- int T;
- scanf("%d",&T);
- bigfor(T) {
- int n,m;
- scanf("%d%d",&n,&m);
- char op[5];
- Build(0,n-1,1);
- // FK("Build Success!");
- for(int i=0; i<m; i++) {
- scanf("%s",op);
- if(op[0]=='S') {
- int l,r;
- int ans=0;
- scanf("%d%d",&l,&r);
- for(int j=0; j<4; j++) ans+=Query(l,r,j,0,n-1,1)<<j;
- // 将每一位的数字和用10进制进位后相加。
- printf("%d\n",ans);
- } else {
- int opn,l,r;
- char v;
- scanf("%d%d%d",&opn,&l,&r);
- if(op[0]=='A') { //AND为&如果某位上为 1 那么值不变 否则全变为0;【区间覆盖】
- for(int j=0; j<4; j++) {
- int x=opn&(1<<j);
- // FK("j=="<<j<<" x=="<<x);
- if(!x)UpData(l,r,0,j,0,n-1,1);
- }
- }
- if(op[0]=='O') { //OR 为|如果某位上为 0 那么值不变 否则全变为1;【区间覆盖】
- for(int j=0; j<4; j++) {
- int x=opn&(1<<j);
- // FK("j=="<<j<<" x=="<<x);
- if(x)UpData(l,r,1,j,0,n-1,1);
- }
- }
- if(op[0]=='X') { //XOR为^如果某位上为 0 那么值不变 否则0->1,1->0【区间更新】
- for(int j=0; j<4; j++) {
- int x=opn&(1<<j);
- // FK("j=="<<j<<" x=="<<x);
- if(x)UpData(l,r,2,j,0,n-1,1);
- }
- }
- }
- }
- }
- return 0;
- }
- //下面是快速读入的福利【只适用于读入比较多的题目,适用于题目原本复杂度为O(n),但是自己的代码估计会是O(nlog(n)),这个优化的作用就会比较明显。】
- //下面就是黑科技:
- namespace IO {
- const int MT = 5e7; //1e711000kb
- char buf[MT];
- int c, sz;
- void begin() {
- c = 0;
- sz = fread(buf, 1, MT, stdin);
- }
- inline bool read(int &t) {
- while(c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;
- if(c >= sz) return false;
- bool flag = 0;
- if( buf[c] == '-')flag = 1, c++;
- for(t = 0; c < sz && '0' <= buf[c] && buf[c] <='9'; c++) t = t * 10 + buf[c] - '0';
- if(flag) t = -t;
- return true;
- } inline bool read(char s[]) {
- while(c < sz && (buf[c] == ' ' || buf[c] == '\n')) c++;
- if(c >= sz) return false;
- int len = 0;
- while(c < sz && buf[c] != ' ' && buf[c] != '\n') s[len++] = buf[c] , c++;
- s[len]=0;
- return true;
- }
- }
- using namespace IO;
- //打开方式:
- int x;
- read(x);
ACM: FZU 2105 Digits Count - 位运算的线段树【黑科技福利】的更多相关文章
- FZU 2105 Digits Count(线段树)
Problem 2105 Digits Count Accept: 302 Submit: 1477 Time Limit: 10000 mSec Memory Limit : 262144 KB P ...
- FZU 2105 Digits Count
Problem 2105 Digits Count Accept: 444 Submit: 2139 Time Limit: 10000 mSec Memory Limit : 2621 ...
- FZU 2105 Digits Count(按位维护线段树)
[题目链接] http://acm.fzu.edu.cn/problem.php?pid=2105 [题目大意] 给出一个序列,数字均小于16,为正数,每次区间操作可以使得 1. [l,r]区间and ...
- FZU 2105 Digits Count(位数计算)
Description 题目描述 Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations: Operation ...
- fzu 2105 Digits Count ( 线段树 ) from 第三届福建省大学生程序设计竞赛
http://acm.fzu.edu.cn/problem.php?pid=2105 Problem Description Given N integers A={A[0],A[1],...,A[N ...
- HDU 6186 CS Course【前后缀位运算枚举/线段树】
[前后缀枚举] #include<cstdio> #include<string> #include<cstdlib> #include<cmath> ...
- FOJ 2105 Digits Count
题意:对一串数字进行抑或某数,和某数,或某数,统计某区间和的操作. 思路:因为化成二进制就4位可以建4颗线段树,每颗代表一位二进制. and 如果该为是1 直接无视,是0则成段赋值为0: or 如 ...
- HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少 ...
- Count the Colors(线段树染色)
Count the Colors Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu Submit ...
随机推荐
- 搭建consul 集群
1. 准备工作 a) 启动三台虚拟机 s1:10.1.7.141 s2:10.1.7.139 s3:10.1.7.138 b) 每台机器上在 /home新建文件夹 mkdir ...
- Eclipse使用Maven tomcat:run命令启动web项目时修改默认端口
- [CodeWars][JS]实现大整数加法
问题描述 实现‘字符串加法’,即将两个以字符串形式表示的数字相加,得到结果然后返回一个新的字符串. 例如:输入‘123’,‘321’,返回‘444’. 这样在进行两个任意大的整数相加的时候,既不会溢出 ...
- ping 或者ssh 发生connect: No buffer space available 错误
如果遇到这种情况,一般说明你的本地服务器的arp表缓存太大,而服务器内核设定的回收条数太小,一直被回收造成的. 可以用一下命令扩大arp表可以缓存的记录条数: echo 512 > /proc/ ...
- shell语法
基本语法列表 #linux组成:内核+工具 #linux启动: . getty:提示登录名和密码,输入之后调用login . login:login验证用户名和密码,然后调用shell . shell ...
- CSS3实现鼠标移动到图片上图片变大
CSS3实现鼠标移动到图片上图片变大(缓慢变大,有过渡效果,放大的过程是有动画过渡的,这个过渡的时间可以自定义 <!DOCTYPE html><html> <head&g ...
- il c井
base.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 将resource 替换成 fileComplier 生成的 resx(可以 ...
- connect-flash 中间件
http://blog.csdn.net/liangklfang/article/details/51086607
- DevExpress GridControl 选择整行被选单元格不变色的设置
设置GridControl 里面的 gridview 属性, 找到OptionSelection 将EnableAppearanceFocusedCell 属性设置False 就可以了 此方式同样适用 ...
- IDEA tomcat乱码
在运行/调试 配置对话框的Startup/Connection面板中, 勾选Pass environment variables. 并添加一个environment variable, Name填 J ...