ETO的公开赛T2《宏聚变》 题解(BY 萌萌哒123456 )

我们注意到这道题中最多有 $(n+q)$ 个数被加入,而每个数最多被删除一次,因此每次操作 $O(logn)$的复杂度是可以接受的。

我们对于$1..100000$之间每个数分别开一个set,维护这个数出现在哪些位置,这样我们就可以非常方便地维护每个数的前驱和后继。

同时我们开一个数组保存每个位置的数是多少。

对于加入操作,我们直接将这个数的坐标加入相应的set,并且从占据这个坐标的数所对应的set中删除这个坐标。

对于删除操作,我们维护两个迭代器(一个向左,一个向右),每次删除距离询问位置较近的一个,然后移动这个迭代器,直到删除次数耗尽为止。

每次加入一个数和删除一个数的复杂度均为 $O(logn)$ ,总复杂度为 $O((n+q)log_(n+q))$

#include<bits/stdc++.h>
#define iter set<int>::iterator
using namespace std;

const int N=200050;
int n,Q,a[N];
set<int> s[N];
vector<int> v;

int read(){
    int x=0;char ch=getchar();
    while(ch<'0' || ch>'9')ch=getchar();
    while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}

int main(){
    n=read();
    for(int i=1,x;i<=n;++i)x=read(),a[i]=x,s[x].insert(i);
    for(Q=read();Q--;){
        int pos=read(),type=read(),E=read(),Dpos=read(),Dtype=read();
        iter Pre=s[type].upper_bound(pos),Nxt=Pre;
        while(E && !s[type].empty() && (Pre!=s[type].begin() || Nxt!=s[type].end())){
            E--;
            int flag=0;
            if(Pre!=s[type].begin()){
                if(Nxt==s[type].end())v.push_back(*(--Pre)),flag=1;
                else if(pos-(*(--Pre))<=(*Nxt)-pos)v.push_back(*Pre),flag=1;
                else ++Pre;
            }
            if(!flag)v.push_back(*Nxt),Nxt++;
        }
        sort(v.begin(),v.end());
        printf("%d ",v.size());
        for(int i=0;i<v.size();i++)printf("%d ",v[i]),s[type].erase(v[i]),a[v[i]]=0;
        puts("");
        v.clear();
        if(a[Dpos])s[a[Dpos]].erase(Dpos);
        a[Dpos]=Dtype;s[Dtype].insert(Dpos);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ztz11/p/9739140.html