#include <stdio.h>
#include <algorithm>
#include <assert.h>
#include <cmath>
#include <deque>
#include <iostream>
#include <limits.h>
#include <map>
#include <math.h>
#include <queue>
#include <set>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <functional>
#include <iomanip>
#include <cstdint>
#define ll long long
#define rep2(i,a,b) for(int i=a;i<=b;++i)
#define rep(i,n) for(int i=0;i<n;i++)
#define rep3(i,a,b) for(int i=a;i>=b;i--)
#define REP(e,v) for(auto e:v)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define tpiii tuple<int,int,int>
#define mp make_pair
#define mt make_tuple
#define pq priority_queue<int>
#define pqg priority_queue<int,vector<int>,greater<int>>
#define pb push_back
#define vec vector
#define vi vec<int>
#define vl vec<ll>
#define vivi vec<vi>
#define vlvl vec<vl>
#define vpii vec<pii>
#define vpll vec<pll>
#define vbl vec<bool>
#define endl "\n"
#define ALL(c) (c).begin(),(c).end()
#define D(x) cout << (#x) << " is " << x << endl;
using namespace std;
int in() {int x;scanf("%d",&x);return x;}
ll lin() {ll x;scanf("%lld",&x);return x;}
string stin(){string s;cin>>s;return s;}
void drop(string s){cout<<s<<endl; exit(0);}
void drop(int x){cout<<x<<endl; exit(0);}
void drop(ll x){cout<<x<<endl; exit(0);}
void drop(double x){cout<<x<<endl; exit(0);}

ll F(ll a,ll b){
    return a xor b;
}
using namespace std;

template<class T,class M>
class SegTreeLazy{
    int n;
    vector<T> data,lazy;
    const T defT; //初期値かつ単位元
    const M defM;
    function<T(T,T)> ftt;
    function<T(T,T)> update;
    function<T(T,M)> ftm;
    function<M(M,M)> fmm;
    
    public:
    SegTreeLazy(size_t _n, T _defT, M _defM, function<T(T,T)> _ftt, function<T(T,T)> _update, function<T(T,M)> _ftm, function<T(M,M)> _fmm)
        :defT(_defT),defM(_defM),ftt(_ftt),update(_update),ftm(_ftm),fmm(_fmm){
        n = 1;
            while(n < _n)n*=2;
            data = vector<T>(2*n-1,defT);
            lazy = vector<M>(2*n-1,defM);
    }
    
    //初期値代入
    void set(int i,T x){
        data[i+n-1] = x;
    }
    void build(){ //セグ木を一通り作ったらこれをする！！！
        for(int i = n-2; i>=0; i--) data[i] = ftt(data[i*2+1],data[i*2+2]);
    }
    
    //遅延評価
    void eval(int k){
        if(lazy[k]==defM) return;
        if(k < n-1){
            lazy[k*2+1] = fmm(lazy[k*2+1], lazy[k]);
            lazy[k*2+2] = fmm(lazy[k*2+2], lazy[k]);
        }
        data[k] = ftm(data[k],lazy[k]);
        lazy[k] = defM;
    }
    T _query(int a,int b,int k,int l,int r){
        eval(k);
        if(r<=a || b<=l)return defT;
        if(a <= l && r <= b)return data[k];
        else{
            T c1 = _query(a,b,2*k+1,l,(l+r)/2);
            T c2 = _query(a,b,2*k+2,(l+r)/2,r);
            return ftt(c1,c2);
        }
    }
    
    //sum of [a,b)
    T query(int a,int b){
        return _query(a,b,0,0,n);
    }
    
    //change of a range
    void _change(int a,int b,M x,int k,int l,int r){
        eval(k);
        if(a <= l && r <= b){
            lazy[k] = fmm(lazy[k],x);
            eval(k);
        }else if (a < r && l < b){
            _change(a,b,x,k*2+1,l,(l+r)/2);
            _change(a,b,x,k*2+2,(l+r)/2,r);
            data[k] = ftt(data[k*2+1],data[k*2+2]);
        }
    }
    void change(int a,int b,M x){
        _change(a,b,x,0,0,n);
    }
    //access : SegTreeLazy型の変数stlについて、stl[i]でi番目の葉の値を返す
    T operator[](int i){
        return query(i,i+1);
    }
};
int main(){
    int n=in(),q=in();
    SegTreeLazy<ll,ll> tree(n,0,0,F,F,F,F);
    rep(i,n){
        tree.set(i,in());
    }
    tree.build();
    rep(i,q){
        int t=in(),x=in()-1,y=in();
        if(t==1){
            tree.change(x,x+1,y);
        }else{
            cout<<tree.query(x,y)<<endl;
        }
    }
}

#include <bits/stdc++.h>
using namespace std;

template <typename T>
class SegmentTree
{
    std::vector<T> arr;
    const std::size_t size;
    const std::function<T(const T, const T)> operation;
    const T initial_value;

    std::size_t calc_size(const std::size_t N) const
    {
        std::size_t n = 1;
        while (n < N + 1)
        {
            n *= 2;
        }
        return n;
    }

    void initialize_array()
    {
        this->arr = std::vector<T>(2 * this->size - 1, this->initial_value);
    }

    T query(const std::size_t a, const std::size_t b, const std::size_t k, const std::size_t l, const std::size_t r) const
    {
        if (r <= a || b <= l)
        {
            return this->initial_value;
        }
        if (a <= l && r <= b)
        {
            return this->arr[k];
        }
        else
        {
            return this->operation(
                this->query(a, b, 2 * k + 1, l, (l + r) / 2),
                this->query(a, b, 2 * k + 2, (l + r) / 2, r));
        }
    }

public:
    SegmentTree(const std::size_t N, const std::function<T(const T, const T)> func, const T init) : size(this->calc_size(N)), operation(func), initial_value(init)
    {
        this->initialize_array();
    }

    void update(const std::size_t idx, const T value)
    {
        std::size_t modify_idx = idx;
        modify_idx += this->size - 1;
        this->arr[modify_idx] = value;
        while (modify_idx > 0)
        {
            modify_idx = (modify_idx - 1) / 2;
            this->arr[modify_idx] = this->operation(this->arr[modify_idx * 2 + 1], this->arr[modify_idx * 2 + 2]);
        }
    }

    T query(const std::size_t idx) const
    {
        return this->arr[idx + this->size - 1];
    }

    // [idx_a, idx_b)
    T query(const std::size_t idx_a, const std::size_t idx_b) const
    {
        return this->query(idx_a, idx_b, 0, 0, this->size);
    }

    T operator[](const std::size_t idx) const
    {
        return this->arr[idx + this->size - 1];
    }
};

int main()
{
    size_t N, Q;
    cin >> N >> Q;
    SegmentTree<int> st(
        N + 1, [](int a, int b) { return a ^ b; }, 0);

    int A;
    for (size_t i = 1; i <= N; i++)
    {
        cin >> A;
        st.update(i, A);
    }

    int T, X, Y;
    for (size_t i = 0; i < Q; i++)
    {
        cin >> T >> X >> Y;
        if (T == 1)
        {
            st.update(X, st[X] ^ Y);
        }
        if (T == 2)
        {

            cout << st.query(X, Y + 1) << endl;
        }
    }
}
