數顏色/維護隊列(帶修莫隊)

數顏色/維護隊列

寫完這題差不多直接1A?(第一次沒吸氧,T了)

題意

  1. 詢問:求區間[l,r][l,r]之間有多少種不同的數字
  2. 修改:修改某個位置的數字
  3. 不強制在線

思路:(帶修莫隊板子)

  1. 基本與普通莫隊一樣,僅僅額外加上了時間這個維度(其實看代碼更好懂),甚至按奇偶排序的小技巧也很好用!
  2. 分塊的大小也有講究(當然也可以採用其他玄學分塊):
    設分塊大小爲aa,莫隊算法時間複雜度主要爲以下三個部分:
    1. l,rl,r塊,tt軸移動的複雜度爲O(n2ta2)=O(nanat)O(\frac{n^2t}{a^2})=O(\frac{n}{a}*\frac{n}{a}*t)
    2. l,rl,r塊,l,rl,r移動複雜度爲O(na)=O(naaa)O(na)=O(\frac{n}{a}*a*a)
    3. ll塊,rr的塊間移動複雜度爲O(n2a)=O(nan)O(\frac{n^2}{a})=O(\frac{n}{a}*n)
    4. 因此三個函數maxmax的最小值當aant3\sqrt[3]{nt}取得,爲O(n4t3)O(\sqrt[3]{n^4t})
  3. 然後就沒什麼特別的啦!
#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 1.4e5+10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;

char s[2];
int N, M, cntp, cntq, len, num;
int c[maxn], cnt[maxn*10], ans[maxn];

struct P{
    int pos, pre, to;
}p[maxn];

struct Q{
    int l, r, t, id;
    friend bool operator < (const Q &a, const Q &b) {
        if((a.l-1)/len!=(b.l-1)/len) return a.l<b.l;
        if((a.r-1)/len!=(b.r-1)/len) { //仍然可以採用奇偶排序
            if((a.l-1)/len%2) return a.r>b.r;
            return a.r<b.r;
        }
        if((a.r-1)/len%2) return a.t>b.t;
        return a.t<b.t;
    }
}q[maxn];

int main() {
    //ios::sync_with_stdio(false); cin.tie(0);
    N=read(), M=read();
    for(int i=1; i<=N; ++i) c[i]=read();
    for(int i=1; i<=M; ++i) {
        scanf("%s", s);
        int l=read(), r=read();
        if(s[0]=='Q') ++cntq, q[cntq]=(Q){l,r,cntp,cntq};
        else {
            ++cntp; p[cntp].pos=l;
            p[cntp].pre=c[p[cntp].pos];
            p[cntp].to=c[p[cntp].pos]=r;
        }
    }
    len=ceil(pow(1.*N*cntq,1./3));
    sort(q+1,q+1+cntq);
    for(int i=cntp; i; --i) c[p[i].pos]=p[i].pre; //從後往前,改回最初的數組
    int l=1, r=0, t=0;
    for(int i=1; i<=cntq; ++i) {
        while(l<q[i].l) num-=!(--cnt[c[l++]]);
        while(l>q[i].l) num+=!(cnt[c[--l]]++);
        while(r<q[i].r) num+=!(cnt[c[++r]]++);
        while(r>q[i].r) num-=!(--cnt[c[r--]]); //下面兩個t的移動就是額外的
        while(t<q[i].t) {
            ++t;
            if(p[t].pos>=l&&p[t].pos<=r) num-=!(--cnt[c[p[t].pos]]);
            c[p[t].pos]=p[t].to;
            if(p[t].pos>=l&&p[t].pos<=r) num+=!(cnt[c[p[t].pos]]++);
        }
        while(t>q[i].t) {
            if(p[t].pos>=l&&p[t].pos<=r) num-=!(--cnt[c[p[t].pos]]);
            c[p[t].pos]=p[t].pre;
            if(p[t].pos>=l&&p[t].pos<=r) num+=!(cnt[c[p[t].pos]]++);
            --t;
        }
        ans[q[i].id]=num;
    }
    for(int i=1; i<=cntq; ++i) printf("%d\n", ans[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章