求区间中不同的数的个数(离线 线段树)

HH的项链

思路:

离线按照r排序,然后把小于r的数字加到线段树中,同时把出现过的a[j]的位置在线段树中删掉

代码:

#define read() FastIO::read()
#define clean() FastIO::flush()
const int N = 1000010;
int a[N];
int tr[N << 2];
int M = 1;
void pushup(int p) {
    tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
void build(int n) {
    for (M = 1; M <= n + 5; M <<= 1);
    //for(int i=i;i<=n;i++) tr[i+M]=a[i];
    for (int i = M; i; i--) {
        pushup(i);
    }
}
void modify(int p, int v) {
    p = p + M;
    tr[p] = v;
    for (p >>= 1; p; p >>= 1) pushup(p);
}
int query(int l, int r) {
    int ans = 0;
    for (l += M - 1, r += M + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
        if (~l & 1) ans += tr[l ^ 1];
        if (r & 1) ans += tr[r ^ 1];
    }
    return ans;
}
struct T {
    int l, r, id;
    bool operator<(const T &t)const {
        return r < t.r;
    }
} q[N];
int ans[N];
int vis[N];
void solve(int Case) {
    int n = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    int m = read();
    build(n);
    for (int i = 1; i <= m; i++) {
        int l = read(), r = read();
        q[i] = {l, r, i};
    }
    sort(q + 1, q + 1 + m);
    for (int i = 1, j = 1; i <= m; i++) {
        auto [l, r, id] = q[i];
        for (; j <= r; j++) {
            if (vis[a[j]]) {
                int p = vis[a[j]];
                modify(p, 0);
                modify(j, 1);
                vis[a[j]] = j;
            } else {
                modify(j, 1);
                vis[a[j]] = j;
            }
        }
        ans[id] = query(l, r);
    }
    for (int i = 1; i <= m; i++) {
        printf("%lld\n", ans[i]);
    }
}

signed main() {
    solve(Case);

    return 0;
}

[HEOI2012]采花

思路:

题意是要求l到r中出现次数超过1次的所有颜色数量,所以只需要对每种颜色维护最近的两个位置(x,y),维护线段树并且把权值加在x上面,按照上题思路更新即可
要点:每次维护两个位置可以保证出现次数,把权值只能加在x这个点上面,加到y上面如果查询的左端点大于x且小于y就会出现错误

代码

const int N = 2000010;
int a[N];
int tr[N << 2];
int M = 1;
void pushup(int p) {
    tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
void build(int n) {
    for (M = 1; M <= n + 5; M <<= 1);
    //for(int i=i;i<=n;i++) tr[i+M]=a[i];
    for (int i = M; i; i--) {
        pushup(i);
    }
}
void modify(int p, int v) {
    p = p + M;
    tr[p] += v;
    for (p >>= 1; p; p >>= 1) pushup(p);
}
int query(int l, int r) {
    int ans = 0;
    for (l += M - 1, r += M + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
        if (~l & 1) ans += tr[l ^ 1];
        if (r & 1) ans += tr[r ^ 1];
    }
    return ans;
}
struct T {
    int l, r, id;
    bool operator<(const T &t)const {
        return r < t.r;
    }
} q[N];
int ans[N];
pair<int, int> vis[N];
void solve(int Case) {
    int n = read(), c = read(), m = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    build(n);
    for (int i = 1; i <= m; i++) {
        int l = read(), r = read();
        q[i] = {l, r, i};
    }
    sort(q + 1, q + 1 + m);
    for (int i = 1, j = 1; i <= m; i++) {
        auto [l, r, id] = q[i];
        for (; j <= r; j++) {
            auto &[x, y] = vis[a[j]];
            if (x > y) swap(x, y);
            if (!x or !y) {
                if (!x) {
                    x = j;
                } else if (!y) {
                    y = j;
                }
                if (x and y) {
                    if (x > y) swap(x, y);
                    modify(x, 1);
                }
            } else {
                int t = min(x, y);
                modify(t, -1);
                if (x > y) swap(x, y);
                x = j;
                swap(x, y);
                modify(x, 1);
            }
            if (x > y) swap(x, y);
        }
        int d = query(l, r);
        ans[id] = d ;
    }
    for (int i = 1; i <= m; i++) {
        printf("%lld\n", ans[i]);
    }
}

signed main() {
    solve(Case);

    return 0;
}

如果说求出现在l到r之间至少k个的数量,就维护k个,更新最小的k权值为1

posted @ 2022-04-26 17:13  指引盗寇入太行  阅读(104)  评论(0编辑  收藏  举报