【BZOJ 2453】维护队列(分块)

博客围绕小朋友A对弹珠队列的操作展开,A想知道某段连续弹珠中不同颜色的数量,还会替换某个弹珠颜色。题目给出输入输出要求及示例,解题思路是采用分块算法,用last[i]表示相同位置,查询时枚举不完整块、二分完整块,修改时重构受影响块。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

Description

你小时候玩过弹珠吗?
小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N。为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少。当然,A有时候会依据个人喜好,替换队列中某个弹珠的颜色。但是A还没有学过编程,且觉得头脑风暴太浪费脑力了,所以向你来寻求帮助。

Input

输入文件第一行包含两个整数N和M。
第二行N个整数,表示初始队列中弹珠的颜色。
接下来M行,每行的形式为“Q L R”或“R x c”,“Q L R”表示A想知道从队列第L个弹珠到第R个弹珠中,一共有多少不同颜色的弹珠,“R x c”表示A把x位置上的弹珠换成了c颜色。

Output

对于每个Q操作,输出一行表示询问结果。

Sample Input

2 3
1 2
Q 1 2
R 1 2
Q 1 2

Sample Output

2
1

HINT

对于100%的数据,有1 ≤ N ≤ 10000, 1 ≤ M ≤ 10000,小朋友A不会修改超过1000次,所有颜色均用1到10^6的整数表示。

原题传送门

思路

能看出来这是一道分块题。

我们用last[i]表示与i相同的上一个的位置,将每个块内的按照last排序。

查询时,暴力枚举不完整的块,二分(lower_bound)每个完整的块。
修改时,直接暴力重构被影响的块。

然后就完事了。。。

代码

#include <bits/stdc++.h>

using namespace std;
const int M = 1010100;
int n, q, m, num;
int a[M], b[M], last[M], Last[M];

inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1)+(x << 3)+(ch ^ 48);
        ch = getchar();
    }
    return x*f;
}

void change(int x, int c) {
    if (a[x] == c) return;
    for (int i = 1; i <= n; i++) b[a[i]] = 0;
    a[x] = c;
    for (int i = 1; i <= n; i++) {
        if (last[i] != b[a[i]]){
        	last[i] = b[a[i]];
            int d = i / m;
            if (i % m) d++;
            int l = (d-1)*m+1, r = min(n, d*m);
            for (int j = l; j <= r; j++) Last[j] = last[j];
            sort(Last+l, Last+r+1);
        }
        b[a[i]] = i;
    }
    return ;
}
  
int ask(int x, int y) {
    int ans = 0;
    if (y-x+1 <= m*2) {
        for(int i = x; i <= y; i++)
            if(last[i] < x) ans++;
        return ans;
    }
    int xx = x / m, yy = y / m;
    if (x % m) xx++; if (y % m) yy++;
    int L = xx+1, R = yy-1;
    if (x == ((xx-1)*m+1)) L--;
    if (y == min(n, yy * m)) R++;
    for (int i = x; i <= (L-1)*m; i++)
        if (last[i] < x) ans++;
    for (int i = min(n,R*m)+1; i <= y; i++)
        if (last[i] < x) ans++;
    for (int i = L; i <= R; i++){
        int l = (i-1)*m+1, r = min(n, i*m);
        ans += lower_bound(Last+l, Last+r+1, x)-(Last+l);
    }
    return ans;
}
  
int main() {
	ios::sync_with_stdio(false);
    n = read(), q = read();
	m = sqrt(n), num = n / m;
	if (n % m) num++;
    for(int i = 1; i <= n; i++) {
        a[i] = read();
        last[i] = Last[i] = b[a[i]];
        b[a[i]] = i;
    }
    for (int i = 1; i <= num; i++) {
        int l = (i-1)*m+1, r = min(n, i*m);
        sort(Last+l, Last+r+1);
    }
    for (int i = 1, l, r, t; i <= q; i++) {
        char c[5]; scanf("%s%d%d", c, &l, &r);
        if (c[0] == 'Q') {
            if (l > r) t = l, l = r, r = t;
            cout << ask(l, r) << endl;
        }
        else change(l, r);
    }
    return 0;
}

过是过了,时间卡的挺紧……
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值