【题解】「Ynoi2016」镜中的昆虫 [*medium]

区间数不同的数的个数是个常见的套路,不说了。

考虑这个区间修改怎么办——意味着需要修改一堆数的 (pre)

假设对于修改前,有一段区间 ([l',r'],lleq l'leq r'leq r) ,满足这一段区间中的数都是相同的。那么进行了修改过后,容易发现改变 (pre) 只有 (pre_{l'},pre_{r''}) ,这里 (pre_{r''}) 满足修改前,(pre_{r''}=r')

接着考虑,如果最开始整个序列有 (n) 个区间,显而易见的,如果一次修改覆盖到了 (a) 个完整的区间并且碰到了 (bleq 2) 个不完整的区间,那么暴力进行修改的话是 (a+b) 次修改的,然后区间数就会减去 (a-1)

由于区间数最多减到 (1),又因为 (bleq 2) ,所以其实暴力修改的次数是 (O(n+m)) 级别的。事实上,最坏情况大概是 (n+2m) 次修改。

所以拿一个 set 维护一下区间,然后维护 (pre) 的话用二维线段树即可。

时间复杂度是 (O((n+m)log n) ?只有 (10^5) 所以应该不会被卡常吧?


关于实现:

  • 二维线段树:最坏点得跑 (2.7s) 。(空间 (log^2)

  • 树状数组 + 动态开点线段树:最坏点 (1.7s),足以通过 loj 数据。(空间 (log ^2))。

  • CDQ + 平衡树:卡住了懒得调。(空间 (log))。

  • CDQ + 树状数组:最坏点 (0.9s),足以通过 luogu 数据。(空间 (log))。

容易发现 luogu 的空间有毒,甚至不足以开下两个 Node 数组搞 CDQ,所以可以开两个 int 数组维护编号(具体见代码实现)

而且为了加速,这里采用归并排序。


关于代码。

树状数组 + 动态开点线段树:详见提交记录

CDQ + 树状数组:

const int N=1e5+5;
const int M=1e6+5;

int n,m,_,cnt,a[N],id[M],ti[M],ans[N],h[N<<1];
struct Query {int tim,l,r,val,typ,op;} q[M];

namespace ChthollyTree { // {{{ ChthollyTree
    #define ins insert

    int pre[N],las[N<<1];
    struct Node {
        int l,r,val;
        bool operator < (const Node &kls) const {return r<kls.l;}
    };
    std::set <Node> seq;
    std::set <pii> sta[N<<1];

    inline void update(int x,int now) {
        q[++cnt]=(Query){cnt,x,-1,pre[x],0,-1},
        q[++cnt]=(Query){cnt,x,-1,pre[x]=now,0,1};
    }
    inline void init() {
        lep(i,1,n) {
            a[i]=std::lower_bound(h+1,h+1+_,a[i])-h;
            q[++cnt]=(Query){cnt,i,-1,pre[i]=las[a[i]],0,1},las[a[i]]=i;
            seq.ins((Node){i,i,a[i]}),sta[a[i]].ins(mkp(i,i));
        }
        lep(i,1,_) sta[i].ins(mkp(0,0));
    }
    inline void insert(int l,int r,int val) {
        std::set <pii>::iterator it2=sta[val].upper_bound(mkp(l,r)),it1=it2; --it2;
        if(it1!=sta[val].end()) update(it1->fi,r); update(l,it2->se);
        sta[val].ins(mkp(l,r)),seq.ins((Node){l,r,val});
    }
    inline void erase(int l,int r,int val,int _flag) {
        std::set <pii>::iterator it2=sta[val].upper_bound(mkp(l,r)),it1=it2; --it2,--it2;
        if(it1!=sta[val].end()) update(it1->fi,it2->se);
        if(l!=_flag) update(l,l-1);
        sta[val].erase(mkp(l,r)),seq.erase((Node){l,r,val});
    }
    inline void cut(int l,int r,int val,int pos) {
        seq.erase((Node){l,r,val}),sta[val].erase(mkp(l,r));
        seq.ins((Node){l,pos,val}),sta[val].ins(mkp(l,pos));
        seq.ins((Node){pos+1,r,val}),sta[val].ins(mkp(pos+1,r)); 
    }
    inline void split(int L,int R,int val) {
        std::set <Node>::iterator it=seq.lower_bound((Node){L,L-1,0});

        if(it->l<=L&&R<=it->r) {
            int l=it->l,r=it->r,_val=it->val;
            if(l<L) cut(l,r,_val,L-1);
            if(R<r) cut(L,r,_val,R);
            erase(L,R,_val,L);
        } else while(it!=seq.end()&&it->l<=R) {
            int l=it->l,r=it->r,_val=it->val; ++it;
            if(L<=l&&r<=R) erase(l,r,_val,L);
            else {
                if(l<L&&L<=r&&r<=R) cut(l,r,_val,L-1),erase(L,r,_val,L);
                if(L<=l&&l<=R&&R<r) cut(l,r,_val,R),erase(l,R,_val,L);
            }
        }
        insert(L,R,val);
    }
} using namespace ChthollyTree; // }}}

// {{{ cdq divide

// {{{ BIT

int res,c[N];
inline int lowbit(int x) {return x&(-x);}
inline void modify(int x,int y) {for(;x<=n;x+=lowbit(x)) c[x]+=y;}
inline int query(int l,int r) {
    --l,res=0;
    while(r>l) res+=c[r],r-=lowbit(r);
    while(l>r) res-=c[l],l-=lowbit(l);
    return res;
}

// }}}

void cdq(int l,int r) {
    if(l==r) return ;
    int mid=(l+r)>>1;
    cdq(l,mid),cdq(mid+1,r);

    int L=mid+1; lep(i,l,mid) {
        while(L<=r&&q[id[L]].val<q[id[i]].val) {
            if(q[id[L]].typ) ans[q[id[L]].typ]+=q[id[L]].op*query(q[id[L]].l,q[id[L]].r);
            ++L;
        }
        if(!q[id[i]].typ) modify(q[id[i]].l,q[id[i]].op);
    }
    while(L<=r) {
        if(q[id[L]].typ) ans[q[id[L]].typ]+=q[id[L]].op*query(q[id[L]].l,q[id[L]].r);
        ++L;
    }
    lep(i,l,mid) if(!q[id[i]].typ) modify(q[id[i]].l,-q[id[i]].op);

    int i=l,j=mid+1,t=l;
    while(i<=mid&&j<=r) ti[t++]=(q[id[i]].val<q[id[j]].val)?id[i++]:id[j++];
    while(i<=mid) ti[t++]=id[i++];
    while(j<=r) ti[t++]=id[j++];
    lep(i,l,r) id[i]=ti[i];
}
// }}}

int query_cnt,_op[N],_l[N],_r[N],_x[N];
int main() {
    IN(n,m);
    lep(i,1,n) IN(a[i]),h[++_]=a[i];
    lep(i,1,m) {
        IN(_op[i],_l[i],_r[i]);
        if(_op[i]==1) h[++_]=_x[i]=int(IN);
    }
    
    std::sort(h+1,h+1+_); int tmp=_; _=1;
    lep(i,2,tmp) if(h[i]!=h[_]) h[++_]=h[i];

    init();
    lep(i,1,m) {
        if(_op[i]==1) split(_l[i],_r[i],std::lower_bound(h+1,h+1+_,_x[i])-h);
        if(_op[i]==2) q[++cnt]=(Query){cnt,_l[i],_r[i],_l[i]-1,++query_cnt,1};
    }
    
    lep(i,1,cnt) id[i]=i;
    cdq(1,cnt);
    lep(i,1,query_cnt) printf("%d
",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/losermoonlights/p/14166040.html