[Ynoi2016] 镜中的昆虫

题目描述

维护一个长为 \(n\) 的序列 \(a_i\),有 \(m\) 次操作。

  1. 将区间 \([l,r]\) 的值修改为 \(x\)

  2. 询问区间 \([l,r]\) 出现了多少种不同的数,也就是说同一个数出现多次只算一个。

\(1\leq n , m \leq 10^5\)\(1\leq a_i\leq 10^9\)

\(\rm 64MB.\)

题解

从题面上看,这是珂朵莉树的非随机加强版,直接上 ODT 肯定会爆,只能另寻他法。

为了计数不重不漏,强制令只有第一次出现的数才有贡献,反之则没有,即:位置 \(i\) 能向区间 \([l,r]\) 贡献当且仅当 \([l\leq i\leq r][pre_i\lt l]=1\),其中 \(pre_i\)\(a_i\) 上次出现的位置,没有则为 \(0\)

从限制的形式上看,这就是一个带修的二维数点,如果这题不卡空间的话,可以直接用树套树解决。但是它卡。

不妨把时间轴也看成一维,那么现在问题就变成三维偏序了,使用分治在 \(O(n\log^2 n)\) 的时间复杂度解决。

代码

#include<bits/stdc++.h>

using namespace std;

#define ssiz(x) (signed)x.size()
#define allc(x) x.begin(),x.end()
const int N=3e5+9;

int a[N],buc[N<<1],pre[N],n,m,tot;
int op[N],ql[N],qr[N],qx[N];

struct Seg{
    int l,r,dat;
    Seg(){}
    Seg(int _l,int _r,int _d){l=_l,r=_r,dat=_d;}
    bool operator <(const Seg x)const{return l<x.l;}
};
set<Seg> cs[N],s;
auto Split(int x){
    auto it=s.lower_bound(Seg(x,x,0));
    if(it!=s.end()&&it->l==x) return it;
    --it;
    int l=it->l,r=it->r,dat=it->dat;
    cs[dat].erase(*it);
    s.erase(it);
    cs[dat].insert(Seg(l,x-1,dat));
    cs[dat].insert(Seg(x,r,dat));
    s.insert(Seg(l,x-1,dat));
    return s.insert(Seg(x,r,dat)).first;
}

array<int,5> c[N<<2];
int p[N<<2],ans[N],tr[N];
void Add(int x,int k){
    x++;
    while(x<=n+1){
        tr[x]+=k;
        x+=x&-x;
    }
}
int Ask(int x){
    x++;
    int sum=0;
    while(x){
        sum+=tr[x];
        x&=x-1;
    }
    return sum;
}
void Solve(int l,int r){
    if(l==r) return ;
    int mid=l+r>>1;
    Solve(l,mid),Solve(mid+1,r);
    sort(p+l,p+mid+1,[](int x,int y){return c[x][1]<c[y][1];});
    sort(p+mid+1,p+r+1,[](int x,int y){return c[x][1]<c[y][1];});
    int i=l,j=mid+1;
    while(j<=r){
        while(i<=mid&&c[p[i]][1]<=c[p[j]][1]){
            if(!c[p[i]][4]) Add(c[p[i]][2],c[p[i]][3]);
            i++;
        }
        if(c[p[j]][4]) ans[c[p[j]][4]]+=Ask(c[p[j]][2])*c[p[j]][3];
        j++;
    }
    while(--i>=l) if(!c[p[i]][4]) Add(c[p[i]][2],-c[p[i]][3]);
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    #define endl '\n'
    
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++){
        cin>>op[i]>>ql[i]>>qr[i];
        if(op[i]==1) cin>>qx[i];
    }

    vector<int> val;
    for(int i=1;i<=n;i++) val.push_back(a[i]);
    for(int i=1;i<=n;i++) if(op[i]==1) val.push_back(qx[i]);
    val.push_back(-1);
    sort(allc(val));
    val.erase(unique(allc(val)),val.end());

    for(int i=1;i<=n;i++) a[i]=lower_bound(allc(val),a[i])-val.begin();
    for(int i=1;i<=m;i++) if(op[i]==1) qx[i]=lower_bound(allc(val),qx[i])-val.begin();
    for(int i=1;i<=n;i++){
        pre[i]=buc[a[i]],buc[a[i]]=i;
        s.insert(Seg(i,i,a[i]));
        cs[a[i]].insert(Seg(i,i,a[i]));
        c[++tot]={0,pre[i],i,1,0};
    }
    for(int i=1;i<=m;i++){
        if(op[i]==2){
            c[++tot]={i,ql[i]-1,ql[i]-1,-1,i};
            c[++tot]={i,ql[i]-1,qr[i],1,i};
            continue ;
        }
        auto ri=Split(qr[i]+1),li=Split(ql[i]);
        for(auto it=li;it!=ri;it++){
            auto jt=cs[it->dat].upper_bound(*it);
            if(jt==cs[it->dat].end()) continue ;
            c[++tot]={i,pre[jt->l],jt->l,-1,0};
            pre[jt->l]=pre[it->l];
            c[++tot]={i,pre[jt->l],jt->l,1,0};
        }
        for(auto it=next(li);it!=ri;it++){
            c[++tot]={i,pre[it->l],it->l,-1,0};
            pre[it->l]=it->l-1;
            c[++tot]={i,pre[it->l],it->l,1,0};
        }
        if(li->dat!=qx[i]){
            auto it=cs[qx[i]].lower_bound(Seg(li->l,li->l,0));
            c[++tot]={i,pre[li->l],li->l,-1,0};
            if(it!=cs[qx[i]].begin()) pre[li->l]=(--it)->r;
            else pre[li->l]=0;
            c[++tot]={i,pre[li->l],li->l,1,0};
        }
        for(auto it=li;it!=ri;it++) cs[it->dat].erase(*it);
        s.erase(li,ri);
        cs[qx[i]].insert(Seg(ql[i],qr[i],qx[i]));
        s.insert(Seg(ql[i],qr[i],qx[i]));
        auto tmp=next(cs[qx[i]].lower_bound(Seg(ql[i],qr[i],qx[i])));
        if(tmp!=cs[qx[i]].end()){
            c[++tot]={i,pre[tmp->l],tmp->l,-1,0};
            pre[tmp->l]=qr[i];
            c[++tot]={i,pre[tmp->l],tmp->l,1,0};
        }
    }

    iota(p+1,p+tot+1,1);
    Solve(1,tot);
    for(int i=1;i<=m;i++) if(op[i]==2) cout<<ans[i]<<endl;

    return 0;
}
posted @ 2025-01-20 18:46  JoeyJiang  阅读(13)  评论(0)    收藏  举报