暑假集训中期测试 Problem G: 维护序列 (线段树)

Description

    现在有一个N个整数组成的序列,这N个整数的标号分别为1, 2, ..., N,对这个序列一共进行两类操作:

    ① 1 x y:表示将第x个和第y个(包括x、y)整数之间的所有整数的二进制的最低位的1变为0,如果某个整数的值为0,则不对这个整数做任何改变。
    ② 2 x y :表示你需要回答第x个和第y个(包括x、y)整数之间的所有整数异或的结果。

Input

    输入包含多组测试数据。
    对于每组测试数据,第一行包含两个正整数N(2<=N<=10^4)、M(1<=M<=10^5),表示这个序列一共有N个整 数,你需要处理M次操作。接下来一行一共有N个不超过2^30的非负整数,依次描述了这个序列中各个整数的初始值。再接下来一共有M行,每行均描述了一种 操作。

Output

    对于每个第②类操作,用一行输出一个整数表示回答的结果。

Sample Input

3 4
3 6 0
2 2 2
2 1 2
1 1 3
2 1 3

Sample Output

6
5
6
 
分析:一开始总在想这题中对区间的操作如何更新区间的关键信息(如果一个一个更新显然会超时),硬是没想出个名堂。后来看了标程才明白,原来对区间的更新就是一个一个进行的,只不过每段区间的更新次数是有一个上限的(29),所以,在每个区间保存一个关键信息(区间中所有元素是否均为0)指示该区间是否需要更新就可以避免许多不必要的更新,从而降低时间复杂度。
View Code
#include <stdio.h>

#define N 10001

int n,m;

int a[N];

int ans[4*N];

bool f[4*N];

void update(int cur)

{

    int ls=cur<<1,rs=cur<<1|1;

    ans[cur]=ans[ls]^ans[rs];

    f[cur]=f[ls]|f[rs];

}

void build(int cur,int x,int y)

{

    int mid=x+y>>1,ls=cur<<1,rs=cur<<1|1;

    if(x==y)

    {

        f[cur]=ans[cur]=a[x];

        return;

    }

    build(ls,x,mid);

    build(rs,mid+1,y);

    update(cur);

}

void change(int cur,int x,int y,int s,int t)

{

    int mid=x+y>>1,ls=cur<<1,rs=cur<<1|1;

    if(!f[cur]) return;

    if(x==y)

    {

        ans[cur]^=ans[cur]&(-ans[cur]);

        f[cur]=ans[cur];

        return;

    }

    if(mid>=s)  change(ls,x,mid,s,t);

    if(mid+1<=t)    change(rs,mid+1,y,s,t);

    update(cur);

}

void query(int cur,int x,int y,int s,int t,int &ret)

{

    int mid=x+y>>1,ls=cur<<1,rs=cur<<1|1;

    if(x>=s && y<=t)

    {

        ret^=ans[cur];

        return;

    }

    if(mid>=s)  query(ls,x,mid,s,t,ret);

    if(mid+1<=t)    query(rs,mid+1,y,s,t,ret);

}

void solve(int opt,int x,int y)

{

    int ret=0;

    if(x>y) {int tmp=x;x=y;y=tmp;}

    if(opt==1)  change(1,1,n,x,y);

    else    query(1,1,n,x,y,ret),printf("%d\n",ret);

}

int main()

{

    int opt,x,y,i;

    while(~scanf("%d%d",&n,&m))

    {

        for(i=1;i<=n;i++)   scanf("%d",&a[i]);

        build(1,1,n);

        while(m--)

        {

            scanf("%d%d%d",&opt,&x,&y);

            solve(opt,x,y);

        }

    }

    return 0;

}

 

 

你可能感兴趣的:(线段树)