SP30906 解題報告

Brilliant11001發表於2024-09-04

題目傳送門

題目大意:

給定一個長度為 \(n\) 的序列,\(q\) 次詢問區間 \([l, r]\) 內只出現過一次的數有多少個。

思路:

很明顯帶修莫隊可以做。

複習一下,帶修莫隊就是在普通莫隊的基礎上加上了時間軸,把操作分為詢問操作和修改操作兩種分別存下來。

因為修改是有順序的,每次修改只會會對它之後的查詢操作有變動,而對它之前的查詢不影響。

令當前時間為 \(t\),若當前時間小於所處理的查詢操作的時間,就將時間往後推進,增添幾個修改,反之時間回溯,減少幾個修改,直到當前的所有變數與所查詢區間重合。

通俗地講,就是再弄一指標,在修改操作上跳來跳去,如果當前修改多了就改回來,改少了就改過去,直到次數恰當為止。

排序也需要加上時間這一關鍵字。

就題而言,因為只要求出現一次的數字個數,所以在加數時若沒出現過則答案 \(+1\),否則若剛好出現過一次就要 \(-1\)。刪數操作大同小異。

\(\texttt{Code:}\)

#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 200010;

int n, m;
int a[N];
int len;
inline int get(int x) {return x / len;}

struct Q{
    int id, l, r, t;
    bool operator <(const Q &o) const{
        if(get(l) != get(o.l)) return l < o.l;
        if(get(r) != get(o.r)) return r < o.r;
        return t < o.t; 
    }
}q[N];
int ttq;
struct M{
    int x, y;
}qc[N];
int ttc;
int cnt[N];
int ans[N];

inline void add(int x, int &res) {
    if(!cnt[x]) res++;
    else if(cnt[x] == 1) res--;
    cnt[x]++;
}

inline void del(int x, int &res) {
    cnt[x]--;
    if(!cnt[x]) res--;
    else if(cnt[x] == 1) res++;
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    int op, x, y;
    while(m--) {
        scanf("%d%d%d", &op, &x, &y);
        if(op == 2) q[++ttq] = {ttq, x + 1, y + 1, ttc};
        else qc[++ttc] = {x + 1, y};
    }
    len = pow(n, 2.0 / 3); //從隊內大佬處學來的玄學塊長
    sort(q + 1, q + ttq + 1);
    int id, l, r, tg;
    for(int i = 0, j = 1, t = 0, k = 1, res = 0; k <= ttq; k++) {
        id = q[k].id, l = q[k].l, r = q[k].r, tg = q[k].t;
        while(i < r) add(a[++i], res);
        while(i > r) del(a[i--], res);
        while(j < l) del(a[j++], res);
        while(j > l) add(a[--j], res);
        while(t < tg) {
            ++t;
            if(qc[t].x >= j && qc[t].x <= i) {
                del(a[qc[t].x], res);
                add(qc[t].y, res);
            }
            swap(a[qc[t].x], qc[t].y);
        }
        while(t > tg) {
            if(qc[t].x >= j && qc[t].x <= i) {
                del(a[qc[t].x], res);
                add(qc[t].y, res);
            }
            swap(a[qc[t].x], qc[t].y);
            --t;
        }
        ans[id] = res;
    }
    for(int i = 1; i <= ttq; i++)
        printf("%d\n", ans[i]);
    return 0;
}

相關文章