题目链接http://acm.fzu.edu.cn/problem.php?pid=2105
Time Limit: 10000 m Sec
Memory Limit : 262144 KB
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?
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 Then m lines, each line must be one of the 4 operations above. (0≤opn≤15) For each test case and for each “SUM” operation, please output the result with a single line. 1 7 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. 题目大意:给你n个元素,m次操作: 第一种做法比较暴力,耗费的时间也会比较久,题目的数据范围非常小,只有16,也就是说会有大量重复的元素,那么我们可以直接暴力更新,对于线段树分父节点,如果它的左右儿子一样,我们就将它的值pushdown为儿子值,然后直接位运算,因为现在的父节点的区间范围的数都是一样了,那么这些数的和就是父节点的区间大小乘上父节点的值了: 所以如果数据量一大,这么写就会T,因为如果每个数都不一样的话它会单个更新每个叶子节点。。。。复杂度爆表 接下来的就是比较优秀的写法了,我们将每个数拆成二进制的形式,每个节点有一个小数组用来记录二进制状态下每一位是0还是1。然后用两个懒标记维护分别记为set和setxor,set为区间置数,维护|(或)及&(与)两个运算的,我们可以发现,当|1的时候不管值是多少,都会是1,那么也就是说我们直接将整个区间置数位1,|0的时候没有变化,当&0的时候,所有的数都是0,我们将区间置数位0,那么也就是说&和|都是可以通过区间置数来完成的。 维护异或的时候就是区间取反了,对区间的每个数取反,那么只有当^1的时候才会有效果。这里就直接放出update的代码,这个是核心,理解了的话pushdown也会很容易理解:(注意拆位!) 以下是暴力AC代码: 以下是比较正解的AC代码:Output
Sample Input
4 4
1 2 4 7
SUM 0 2
XOR 5 0 0
OR 6 0 3
SUM 0 2Sample Output
18Hint
操作1:SUM l r
操作2:OR val l r
操作3:XOR val l r
操作4:AND val l r
每次对SUM做出回答。void update(int l,int r,int rt,int mark,int L,int R,int val)
{
if (l>=L && r<=R && tree[rt]!=-1){//注意!!!
if (mark==1) tree[rt]^=val;
else if (mark==2) tree[rt]&=val;
else tree[rt]|=val;
return;
}
int mid=(l+r)>>1;
if (tree[rt]!=-1){
tree[ls]=tree[rs]=tree[rt];tree[rt]=-1;
}
if (mid>=L) update(lson,mark,L,R,val);
if (mid<R) update(rson,mark,L,R,val);
pushup(rt);
//pushup:if (tree[ls]!=-1 && tree[ls]==tree[rs])
// tree[rt]=tree[ls]
}
void update(int l,int r,int rt,int L,int R,int pos,int val)
{//pos代表二进制下第pos位
if (l>=L && r<=R){
if (val==-1) {
tree[rt].sum[pos]=(r-l+1)-tree[rt].sum[pos];//区间每个数的第pos位取反,0->1更新为区间长度-以前的1的个数
if (tree[rt].set[pos]!=-1)
tree[rt].set[pos]^=1;//如果存在区间置数的话,这个区间的值都是set[pos],那么我们直接对标记取反就好了
else tree[rt].setxor[pos]^=1;
}
else {
tree[rt].sum[pos]=(r-l+1)*val;//&0的话区间全部置为0,|1的话全部置为1,否则不变
tree[rt].set[pos]=val;//区间置数
tree[rt].setxor[pos]=0;//由于区间置数的存在,取反没有意义了,无论如何值都会是val
}
return;
}
pushdown(l,r,rt,pos);
int mid=(l+r)>>1;
if (mid>=L) update(lson,L,R,pos,val);
if (mid<R) update(rson,L,R,pos,val);
pushup(rt,pos);
}
****
void pushdown(int l,int r,int rt,int pos)
{
int mid=(l+r)>>1;
if (tree[rt].set[pos]!=-1){//区间置数0,1标记传下
tree[ls].set[pos]=tree[rs].set[pos]=tree[rt].set[pos];
tree[ls].sum[pos]=(mid-l+1)*tree[rt].set[pos];
tree[rs].sum[pos]=(r-mid)*tree[rt].set[pos];
tree[rt].set[pos]=-1;
tree[rt].setxor[pos]=tree[ls].setxor[pos]=tree[rs].setxor[pos]=0;
}
else if (tree[rt].setxor[pos]){
tree[ls].sum[pos]=(mid-l+1)-tree[ls].sum[pos];
tree[rs].sum[pos]=(r-mid)-tree[rs].sum[pos];
if (tree[ls].set[pos]!=-1)
tree[ls].set[pos]^=1;
else tree[ls].setxor[pos]^=1;
if (tree[rs].set[pos]!=-1)
tree[rs].set[pos]^=1;
else tree[rs].setxor[pos]^=1;
tree[rt].setxor[pos]=0;
}
}
#include
#include