题目链接:https://nanti.jisuanke.com/t/40260
题目大意:
给两个长度为n的数组,数字大小不超过1e5,求出它们两两相或的数组c,从小到达排序后支持两种操作:
0 x :查询位置为x的数的当前值
l r:对[l,r]区间的数开方

思路:我们通过fwt得到c数组。c[i]:i的个数。区间开方肯定用线段树维护。问题是区间长度为n * n。我们考虑对于操作:
1 10000000000
0 5
我们真的要建长度为10000000000的线段树吗?其实我们就用到1, 5, 10000000000。3个点。我们肯定考虑离散化。在操作中出现的点保留。开一个2*n的线段树就可以了。

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

LL N;
struct FWT_{
    void FWT_or(LL *a,LL opt) {
    for(LL i=1; i<N; i<<=1)
        for(LL p=i<<1,j=0; j<N; j+=p)
            for(LL k=0; k<i; ++k)
                if(opt==1)
                    a[i+j+k]=a[j+k]+a[i+j+k];
                else
                    a[i+j+k]=a[i+j+k]-a[j+k];
    }
    void ans_or(LL cnt1[], LL cnt2[]){
        FWT_or(cnt1, 1); FWT_or(cnt2, 1);
        for(LL i = 0; i < N; ++i) cnt1[i] = cnt1[i]*cnt2[i];
        FWT_or(cnt1, -1);
//        LL s=0;
//        for(LL i=0; i<N; i++){
//            s+=cnt1[i];
//            printf("%-3lld ", s);
//        }
//        printf("\n");
//        for(LL i=0; i<N; i++){
//            printf("%-3lld ", i);
//        }
//        printf("\n");

    }
}fwt;

struct SegTree {
    LL T[200005<<2], sum[200005<<2];
    //clear_Tree () { memset(T,0,sizeof(T)); memset(sum,0,sizeof(sum)); }
    inline void pushup(LL o) { T[o]=max(T[o<<1],T[o<<1|1]); sum[o]=sum[o<<1]+sum[o<<1|1]; }
    inline void up_data(LL o,LL l,LL r,LL x, LL y){
        if(l==r){
            T[o]=sum[o]=y;
            return ;
        }
        LL mid=l+r>>1;
        if(x<=mid) up_data(o<<1,l,mid,x, y);
        else up_data(o<<1|1,mid+1,r,x, y);
        pushup(o);
    }
    inline void add(LL o,LL l,LL r, LL ql, LL qr){//ql qr开方
        if(T[o]==1) return ;
        if(l==r){
            T[o]=sqrt(T[o]);
            sum[o]=T[o];
            return ;
        }
        LL mid=l+r>>1;
        if(qr<=mid) add(o<<1,l,mid,ql,qr);
        else if(ql>mid) add(o<<1|1,mid+1,r,ql,qr);
        else add(o<<1,l,mid,ql,mid), add(o<<1|1,mid+1,r,mid+1,qr);
        pushup(o);
    }
    inline LL query(LL o,LL l,LL r,LL x){//查询a[x]
        if(T[o]==1) return 1;
        if(l==r) return T[o];
        LL mid=l+r>>1;
        if(x<=mid) return query(o<<1,l,mid,x);
        else return query(o<<1|1,mid+1,r,x);
    }
}T;

struct Node{
    LL x, y;
    LL p;//0查询 1修改
}q[200005];

LL a[200005], b[200005], cnt1[200005], cnt2[200005];
LL c[200005], tot=0;
int main() {
    LL  n; scanf("%lld", &n);
    LL mx=0;
    for(LL i=0; i<n; i++){
        scanf("%lld", &a[i]);
        cnt1[a[i]]++;
        mx=max(a[i], mx);
    }
    for(LL i=0; i<n; i++){
        scanf("%lld", &b[i]);
        cnt2[b[i]]++;
        mx=max(b[i], mx);
    }
    N = 1;while(N <= mx) N<<=1;
    fwt.ans_or(cnt1, cnt2);//fwt

    LL Q; scanf("%lld", &Q);
    for(LL i=1; i<=Q; i++){//保存操作
        LL x, y; scanf("%lld%lld", &x, &y);
        if(x==0){
            q[i].p=0, q[i].x=y;
            c[++tot]=y;
        }
        else{
            q[i].p=1, q[i].x=x, q[i].y=y;
            c[++tot]=x; c[++tot]=y;
        }
    }
    sort(c+1, c+tot+1);
    LL cnt=unique(c+1, c+tot+1)-c-1;
    for(LL i=1; i<=Q; i++){//离散化
        if(q[i].p==0){
            q[i].x=lower_bound(c+1, c+cnt+1, q[i].x)-c;
        }
        else{
            q[i].x=lower_bound(c+1, c+cnt+1, q[i].x)-c;
            q[i].y=lower_bound(c+1, c+cnt+1, q[i].y)-c;
        }
    }
    LL s=0, pos=0;
    for(LL i=1; i<=cnt; i++){
        while(s<c[i]){
            s+=cnt1[++pos];
        }
        T.up_data(1, 1, cnt, i, pos);//把需要的点加入线段树
    }
    for(LL i=1; i<=Q; i++){
        if(q[i].p==0){
            printf("%lld\n", T.query(1, 1, cnt, q[i].x));
        }
        else{
            T.add(1, 1, cnt, q[i].x, q[i].y);
        }
    }

    return 0;
}