Skip to content

P1903 [国家集训队] 数颜色 / 维护队列(带修莫队) ​

题意:
原来的基础上,加上修改某个数的操作
思路:

  • 二关键字变三关键字
  • 首先按l的块编号排序,再按r的块编号排序,最后按修改版本t排序
  • 对于l指针,块内移动为 $ B $,总次数为 $ mB $,块间移动为 $ 2 \cdot B \cdot \frac{n}{B}=2n $,总复杂度为 $ O(mB+n) $
  • 对于r指针,块内移动为 $ mB $,块间移动为 $ \frac{n^2}{B} $
  • 对于t指针,移动次数为 $ \frac{n^2}{B^2}t $
  • dalao证明 $ B= \sqrt[3] {nt} $ 最优
  • 块长取 $ n^{\frac{2}{3}} 比 \sqrt n $ 更优,总复杂度为 $ O(n^{\frac{5}{3}}) $
  • 修改操作优化,将原数组的数与修改数交换即可
cpp
struct Node{
    int id,l,r,t;
};
int cnt[N],w[M];
int be[M],L[M],R[M],tot,block,res;
bool cmp(Node x,Node y){
    return (be[x.l]^be[y.l])?be[x.l]<be[y.l]:((be[x.r]^be[y.r]?be[x.r]<be[y.r]:x.t<y.t));
}
void add(int x){
    if(!(cnt[x]++))res++;
}
void del(int x){
    if(!(--cnt[x]))res--;
}
void solve(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>w[i];
    vector<Node> q;
    vector<array<int,2>> c={{0,0}};
    int que=0;
    for(int i=0;i<m;i++){
        char op;
        int x,y;
        cin>>op>>x>>y;
        if(op=='Q'){
            q.push_back({que++,x,y,c.size()-1});
        }
        else{
            c.push_back({x,y});
        }
    }
    vector<int> ans(que);
    block=pow(n,2.0/3.0),tot=(n+block-1)/block;
    for(int i=1;i<=tot;i++){
        int l=(i-1)*block+1,r=min(i*block,n);
        for(int j=l;j<=r;j++)be[j]=i;
    }
    sort(q.begin(),q.end(),cmp);
    for(int i=0,l=1,r=0,t=0;i<q.size();i++){
        auto [id,ql,qr,qt]=q[i];
        while(r<qr)add(w[++r]);
        while(r>qr)del(w[r--]);
        while(l<ql)del(w[l++]);
        while(l>ql)add(w[--l]);
        while(t<qt){
            t++;
            if(ql<=c[t][0]&&qr>=c[t][0])del(w[c[t][0]]),add(c[t][1]);
            swap(w[c[t][0]],c[t][1]);
        }
        while(t>qt){
            if(ql<=c[t][0]&&qr>=c[t][0])del(w[c[t][0]]),add(c[t][1]);
            swap(w[c[t][0]],c[t][1]);
            t--;
        }
        ans[id]=res;
    }
    for(auto it:ans)cout<<it<<"\n";
}