ACM: FZU 2105 Digits Count - 位运算的线段树【黑科技福利】

时间:2022-01-19 09:41:42
 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

For each test case and for each "SUM" operation, please output the result with a single line.

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 - 位运算的线段树【黑科技福利】的更多相关文章

  1. FZU 2105 Digits Count&lpar;线段树&rpar;

    Problem 2105 Digits Count Accept: 302 Submit: 1477 Time Limit: 10000 mSec Memory Limit : 262144 KB P ...

  2. FZU 2105 Digits Count

     Problem 2105 Digits Count Accept: 444    Submit: 2139 Time Limit: 10000 mSec    Memory Limit : 2621 ...

  3. FZU 2105 Digits Count(按位维护线段树)

    [题目链接] http://acm.fzu.edu.cn/problem.php?pid=2105 [题目大意] 给出一个序列,数字均小于16,为正数,每次区间操作可以使得 1. [l,r]区间and ...

  4. FZU 2105 Digits Count(位数计算)

    Description 题目描述 Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations: Operation ...

  5. fzu 2105 Digits Count &lpar; 线段树 &rpar; from 第三届福建省大学生程序设计竞赛

    http://acm.fzu.edu.cn/problem.php?pid=2105 Problem Description Given N integers A={A[0],A[1],...,A[N ...

  6. HDU 6186 CS Course【前后缀位运算枚举&sol;线段树】

    [前后缀枚举] #include<cstdio> #include<string> #include<cstdlib> #include<cmath> ...

  7. FOJ 2105 Digits Count

    题意:对一串数字进行抑或某数,和某数,或某数,统计某区间和的操作. 思路:因为化成二进制就4位可以建4颗线段树,每颗代表一位二进制. and 如果该为是1  直接无视,是0则成段赋值为0: or  如 ...

  8. HDU 4911 http&colon;&sol;&sol;acm&period;hdu&period;edu&period;cn&sol;showproblem&period;php&quest;pid&equals;4911&lpar;线段树求逆序对)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少 ...

  9. Count the Colors(线段树染色)

    Count the Colors Time Limit:2000MS    Memory Limit:65536KB    64bit IO Format:%lld & %llu Submit ...

随机推荐

  1. css 隔行变色,表单布局

    隔行变色: #list1 li:nth-of-type(odd){ background:#00ccff;}奇数行  #list1 li:nth-of-type(even){ background:# ...

  2. java&period;net&period;URL请求远程文件下载

    1:浏览器请求下载 public void listStockcodeUplaod(HttpServletRequest req, HttpServletResponse res) throws Ex ...

  3. Linux 下安装 jdk压缩包

    按 esc  退出  记得 输入 :q    记得 : Linux下安装jdk 把压缩文件放在 桌面的soft 下 在usr目录下新建文件夹Java,把文件复制到 usr/java 更改下路径! 3 ...

  4. delegate基于on

    前几天看到事件委托的时候,关于live()方法讲的不是很详细,就去搜了一下关于live()和delegate()的,最后看源码发现bind()和delegate()都是由on()实现的,感兴趣的朋友可 ...

  5. lightOJ 1366 Pair of Touching Circles(统计矩形内相切圆对)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1366 题意:给出一个矩形,在内部画两个圆A和B使得AB都完全在矩形内且AB相切且AB的 ...

  6. 团体程序设计天梯赛-练习集L2-008&period; 最长对称子串

    L2-008. 最长对称子串 时间限制 100 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 对给定的字符串,本题要求你输出最长对称子串的长度. ...

  7. FORM - 实现某数据项自动按记录序号加一操作

    ---- 设块名为VO,要操作的数据项为VO_ID,在该块中建立块级触发子WHEN-CREATE- RECORD,每当生成新记录时VO_ID就会自动加一了,加入如下代码: :VO_ID:=:Syste ...

  8. 修改Vim内注释字体颜色

    vim /etc/vimrc   然后按大写  G 到最后一行,插入 hi comment ctermfg=6          wq保存退出 PS:默认的注释颜色是4  然后有0,1,2,3,4,5 ...

  9. linux报错汇总

    一.出现cannot send message: Process exited with a non-zero status错误 查看log文件:sudo cat /var/log/mail.err, ...

  10. Delphi 实现自动更新

    Delphi 通用程序自动更新升级:http://www.delphitop.com/html/wangluo/2968.html https://www.cnblogs.com/hnxxcxg/p/ ...