题目链接http://acm.fzu.edu.cn/problem.php?pid=2105
Time Limit: 10000 m Sec
Memory Limit : 262144 KB
Problem 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.
题目大意:给你n个元素,m次操作:
操作1:SUM l r
操作2:OR val l r
操作3:XOR val l r
操作4:AND val l r
每次对SUM做出回答。
第一种做法比较暴力,耗费的时间也会比较久,题目的数据范围非常小,只有16,也就是说会有大量重复的元素,那么我们可以直接暴力更新,对于线段树分父节点,如果它的左右儿子一样,我们就将它的值pushdown为儿子值,然后直接位运算,因为现在的父节点的区间范围的数都是一样了,那么这些数的和就是父节点的区间大小乘上父节点的值了:
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]
}
所以如果数据量一大,这么写就会T,因为如果每个数都不一样的话它会单个更新每个叶子节点。。。。复杂度爆表
接下来的就是比较优秀的写法了,我们将每个数拆成二进制的形式,每个节点有一个小数组用来记录二进制状态下每一位是0还是1。然后用两个懒标记维护分别记为set和setxor,set为区间置数,维护|(或)及&(与)两个运算的,我们可以发现,当|1的时候不管值是多少,都会是1,那么也就是说我们直接将整个区间置数位1,|0的时候没有变化,当&0的时候,所有的数都是0,我们将区间置数位0,那么也就是说&和|都是可以通过区间置数来完成的。
维护异或的时候就是区间取反了,对区间的每个数取反,那么只有当^1的时候才会有效果。这里就直接放出update的代码,这个是核心,理解了的话pushdown也会很容易理解:(注意拆位!)
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;
}
}
以下是暴力AC代码:
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1
const int mac=1e6+10;
int tree[mac<<2];
void pushdown(int rt)
{
if (tree[ls]!=-1 && tree[ls]==tree[rs])
tree[rt]=tree[ls];
}
void build(int l,int r,int rt)
{
tree[rt]=-1;
if (l==r){
scanf ("%d",&tree[rt]);
return;
}
int mid=(l+r)>>1;
build(lson);build(rson);
pushdown(rt);
}
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);
pushdown(rt);
}
int query(int l,int r,int rt,int L,int R)
{
int ans=0;
if (l>=L && r<=R && tree[rt]!=-1){
return tree[rt]*(r-l+1);
}
int mid=(l+r)>>1;
if (tree[rt]!=-1){
tree[ls]=tree[rs]=tree[rt];tree[rt]=-1;
}
if (mid>=L) ans+=query(lson,L,R);
if (mid<R) ans+=query(rson,L,R);
return ans;
}
int main()
{
int t;
scanf ("%d",&t);
while (t--){
int n,m;
scanf ("%d%d",&n,&m);
build(1,n,1);
char s[5];
for (int i=1; i<=m; i++){
scanf ("%s",s);
//printf ("++%c++",s[0]);
int l,r,val;
if (s[0]=='S'){
scanf ("%d%d",&l,&r);
l++;r++;
//printf ("%d %d",l,r);
int ans=query(1,n,1,l,r);
printf ("%d\n",ans);
}
else {
scanf ("%d%d%d",&val,&l,&r);
l++;r++;
if (s[0]=='X')
update(1,n,1,1,l,r,val);
else if (s[0]=='A')
update(1,n,1,2,l,r,val);
else
update(1,n,1,3,l,r,val);
}
}
}
return 0;
}
以下是比较正解的AC代码:
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1
#define ll long long
const int mac=1e6+10;
struct node
{
int sum[4],set[4],setxor[4];
}tree[mac<<2];
int a[mac];
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;
}
}
void pushup(int rt,int pos)
{
tree[rt].sum[pos]=tree[ls].sum[pos]+tree[rs].sum[pos];
}
void build(int l,int r,int rt)
{
for (int i=0; i<4; i++){
tree[rt].set[i]=-1;
tree[rt].setxor[i]=0;//异或0的话是不变的
tree[rt].sum[i]=0;
}
if (l==r){
for (int i=0; i<4; i++)
if (a[l]&(1<<i))
tree[rt].sum[i]++;
return;
}
int mid=(l+r)>>1;
build(lson);build(rson);
for (int i=0; i<4; i++)
pushup(rt,i);
}
void update(int l,int r,int rt,int L,int R,int pos,int val)
{
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);
}
int query(int l,int r,int rt,int L,int R,int pos)
{
int ans=0;
if (l>=L && r<=R) return tree[rt].sum[pos];
int mid=(l+r)>>1;
pushdown(l,r,rt,pos);
if (mid>=L) ans+=query(lson,L,R,pos);
if (mid<R) ans+=query(rson,L,R,pos);
return ans;
}
int main()
{
// freopen("in.txt","r",stdin);
int t;
scanf ("%d",&t);
while (t--){
int n,m;
scanf ("%d%d",&n,&m);
for (int i=1; i<=n; i++)
scanf ("%d",&a[i]);
build(1,n,1);
for (int i=1; i<=m; i++){
char s[5];
scanf ("%s",s);
int l,r,val;
if (s[0]=='S'){
scanf ("%d%d",&l,&r);
l++;r++;
int ans=0;
for (int j=0; j<4; j++)
ans+=query(1,n,1,l,r,j)*(1<<j);
printf ("%d\n",ans);
}
else if (s[0]=='O'){//or 或
scanf ("%d%d%d",&val,&l,&r);
l++;r++;
for (int j=0; j<4; j++)
if (val&(1<<j))
update(1,n,1,l,r,j,1);
}
else if (s[0]=='X'){//xor 异或
scanf ("%d%d%d",&val,&l,&r);
l++;r++;
for (int j=0; j<4; j++)
if (val&(1<<j))
update(1,n,1,l,r,j,-1);
}
else {//and 与
scanf ("%d%d%d",&val,&l,&r);
l++;r++;
for (int j=0; j<4; j++)
if (!(val&(1<<j)))
update(1,n,1,l,r,j,0);
}
}
}
return 0;
}