Sequence Time Limit(2019南昌邀请赛F 树状数组 区间异或)

本文介绍了一道名为SequenceTimeLimit的算法题,通过使用树状数组解决区间异或问题。文章详细阐述了如何利用两个树状数组分别记录奇数和偶数下标的异或和,从而高效地处理区间更新和查询。

Sequence Time Limit(2019南昌邀请赛F 树状数组 区间异或)

题目描述
有一个长度为?的序列?,其中第 ? 个数字为??。他定义了一个函数?(?,?) = ?? ⊕ ??1 ⊕…⊕ ??.(即第?个数字到第?个数字的异或和)。这个函数的计算对于天才少年 Oldjang 来说太简单了,于是他想要把这个问题改的更难一点。他想要进行如下操作:
1.这个操作被表示为 "0 ? ? ", 意思是将第?个数字的权值改为?;
2.这个操作被表示为 "1 ? ? ",意思是计算函数?(?,?)。?(?,?) = ?(?,?) ⊕ ?(?,? + 1) ⊕ …⊕ ?(?,?) ⊕ ?(? + 1,? + 1) ⊕…?(? + 1,?) ⊕…⊕ ?(?,?)。即?(?,?) 使所有满足? ≤ ? ≤ ? ≤ ? 的?(?,?)的异或和。 Oldjang觉得这个问题还是太简单了,所以他想用这个问题来考考你。
输入
第一行包含一个整数 ? (1 ≤ ? ≤ 10) — 测试数据的组数 . 对于每一组测试数据: 第一行包含两个整数?,?(1 ≤ ?,? ≤ 105), 表示这个序列有?个数字,Oldjang 要进行?次操 作。 接下来一行?个数字表示序列?,所有数值均在 int 范围内。 接下来?行每一行包含"0 ? ? "或者"1 ? ? ",表示一次操作。
输出
对于每一组测试数据,先输出 "???? #?: " 其中 ? 是测试数据的编号(从 1 开始编号). 然后对于每一次第二类操作数据你所计算出的答案。
样例输入
1
3 3
1 2 3
1 1 3
0 1 2
1 1 1
样例输出
Case #1:
2
2

题目大意
长度为n的序列,m次操作,输入0时 修改第x个数字变为y,输入1时 求符合该区间的所有子区间的异或和。
思路
查询时l-r区间如果区间长度为偶数区间异或和为0,区间长度为奇数时,只需要计算[l,r]中有与l奇偶性相同的下标的数的异或和即可。
很明显暴力会超时,所以用2个树状数组,一个存下标为奇数的异或和,一个存下标为偶数的异或和。
卡点

  1. T组循环,不要忘记清零
  2. 修改值时需要异或p[x]再异或y
  3. 修改完成后不要忘记修改p[x]的值 切记 切记 切记(一直wa在这里 想哭)
#include<bits/stdc++.h>
#define mem(a,x) memset(a,x,sizeof(a))
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long ll;
typedef unsigned long long ull; // %llu
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = -1u>>1;
const int maxn = 1e6+5;
inline int read()
{
    char ch = getchar();
    int x = 0, f = 1;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while('0' <= ch && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
int a[maxn];//奇数下标的树状数组
int b[maxn];//偶数下标的树状数组
int p[maxn];//原数组
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int y,int arr[],int len)     //x为树状数组下标 y为需要异或的数 arr为传入的数组 len为arr数组长度
{
    while(x<=len)
    {
        arr[x]^=y;
        x+=lowbit(x);
    }
}
int qurey(int x,int arr[])      //查询x
{
    int sum=0;
    while(x)
    {
        sum^=arr[x];
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    int cas=0;
    int T;
    T=read();
    while(T--)
    {
        mem(a,0);
        mem(b,0);
        cas++;
        printf("Case #%d:\n",cas);
        int n,m,la=0,lb=0;
        n=read(),m=read();
        int alen=(n+1)/2,blen=n-alen;
        for(int i=1; i<=n; i++)
        {
            p[i]=read();
            if(i%2)
            {
                add(++la,p[i],a,alen);
            }
            else
            {
                add(++lb,p[i],b,blen);
            }
        }
        while(m--)
        {
            int op,x,y;
            op=read(),x=read(),y=read();
            if(op==1)
            {
                if((y-x+1)%2==0)
                    printf("0\n");
                else
                {
                    if(x%2)
                    {
                        int xx=(x+1)/2-1,yy=(y+1)/2;
                        int ans=qurey(yy,a)^qurey(xx,a);
                        printf("%d\n",ans);

                    }
                    else
                    {
                        int xx=x/2-1,yy=y/2;
                        int ans=qurey(yy,b)^qurey(xx,b);
                        printf("%d\n",ans);
                    }
                }
            }
            else
            {
                if(x%2)
                {
                    add((x+1)/2,y^p[x],a,alen);
                }
                else
                {
                    add(x/2,y^p[x],b,blen);
                }
                p[x]=y;         //不要忘记修改原数组的值
            }
        }
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值