Strange Hobby(动态开点线段树)

                                   Strange Hobby

题意:

已知n(1e5)个数,有q(1e5)个操作,操作1修改数组的某一数,操作2,l,r,x问有多少个在l到r子区间里x的个数为奇数个。

题解:

因为询问是对数x来找子区间,而数的个数最多2e5,因此可以动态的开点维护这么多线段树,这样空间复杂度是nlogn,不会超,更新线段树要将数为x的线段树对应的位置进行01修改,为了求子区间个数,线段树维护什么呢?区间长度len,区间和sum,区间答案(题目所求)ans,必取左端点的区间答案ansl,必取右端点的区间答案ansr,更新ans=ls(ans)+rs(ans)+ls(ansr)*(rs(len)-rs(ansl))+(ls(len)-ls(ansr))*rs(ansl);其它的更新就更简单了,更新的时候要注意区间长度,因为是动态开点,有的点可能没开到,但是区间长度是固定的。

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+9;
int n,q,a[N];
unordered_map<int,int>mp;int nm;
struct T{
    int len,sum;
    ll ans,ansl,ansr;
}v[N<<5];
int tot,rt[N],L[N<<5],R[N<<5];
T operator+(T t1,T t2){
    T t;
    t.len=t1.len+t2.len;
    t.sum=t1.sum+t2.sum;
    t.ansl=t1.ansl;
    if(t1.sum%2)t.ansl+=t2.len-t2.ansl;else t.ansl+=t2.ansl;
    t.ansr=t2.ansr;
    if(t2.sum%2)t.ansr+=t1.len-t1.ansr;else t.ansr+=t1.ansr;
    t.ans=t1.ans+t2.ans;
    t.ans+=t1.ansr*(t2.len-t2.ansl);
    t.ans+=(t1.len-t1.ansr)*t2.ansl;
    return t;
}
void update(int &now,int l,int r,int qx,int qy){
    if(!now)now=++tot;
    if(l==r){v[now].sum=v[now].ans=v[now].ansl=v[now].ansr=qy;return;}
    int mid=(l+r)>>1;
    if(qx<=mid)update(L[now],l,mid,qx,qy);
    else update(R[now],mid+1,r,qx,qy);
    v[L[now]].len=mid-l+1,v[R[now]].len=r-mid;
    v[now]=v[L[now]]+v[R[now]];
}
T query(int now,int l,int r,int ql,int qr){
    if(now==0)return (T){0,0,0,0,0};
    if(ql<=l&&r<=qr)return v[now];
    else{
        int mid=(l+r)>>1;
        T t=(T){0,0,0,0,0},tt;
        if(ql<=mid)tt=query(L[now],l,mid,ql,qr),tt.len=mid-max(ql,l)+1,t=t+tt;
        if(qr>mid)tt=query(R[now],mid+1,r,ql,qr),tt.len=min(qr,r)-mid,t=t+tt;
        return t;
    }
}
int main(){
 //   freopen("tt.in","r",stdin),freopen("tt.out","w",stdout);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(!mp[a[i]])mp[a[i]]=++nm;
        update(rt[mp[a[i]]],1,n,i,1);
    }
    cin>>q;
    while(q--){
        int opt,x,v,l,r;
        cin>>opt;
        if(opt==1){
            cin>>x>>v;
            update(rt[mp[a[x]]],1,n,x,0),a[x]=v;
            if(!mp[v])mp[v]=++nm;
            update(rt[mp[a[x]]],1,n,x,1);
        }else{
            cin>>l>>r>>x;
            cout<<query(rt[mp[x]],1,n,l,r).ans<<endl;
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值