求区间内不同数的个数(莫队)

题目:https://www.acwing.com/problem/content/2494/

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
//#include <unordered_map>
//#include <unordered_set>
//#include <bits/stdc++.h>
//#define int long long
#define pb push_back
#define PII pair<int, int>
#define mpr make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
#define x first
#define y second
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
using namespace std;
const int N = 50010, M = 200010, S = 1000010;
int n, m, len;
int w[N], ans[M];  // w存输入的n个数,ans存询问的那个答案
struct node {
    int id, l, r;  // id代表第几个询问
} q[M];            //记录询问
int cnt[S];        //记录这个数在这个区间出现了几次
//分块函数
int get(int x) { return x / len; }
//先按照块排序,块内在按照r排序,排序方式分奇偶会更快一些
bool cmp(const node &a, const node &b) {
    int i = get(a.l), j = get(b.l);  //得到块的编号
    if (i != j) return i < j;
    if (i % 2 == 0) return a.r < b.r;  //分奇偶会快一点
    return a.r > b.r;
}
//增加判断
void add(int x, int &res) {
    if (!cnt[x]) res++;
    cnt[x]++;
}
//减小判断
void del(int x, int &res) {
    cnt[x]--;
    if (!cnt[x]) res--;
}
// signed main(){
int main(int argc, char const *argv[]) {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
    len = sqrt(n);
    scanf("%d", &m);
    for (int i = 0; i < m; i++) {
        int l, r;
        scanf("%d%d", &l, &r);
        q[i] = {i, l, r};
    }
    // int res = 0;
    sort(q, q + m, cmp);  //根据规则排序
    for (int k = 0, i = 0, j = 1, res = 0; k < m; k++) {
        int id = q[k].id, l = q[k].l, r = q[k].r;
        while (i < r) add(w[++i], res);  // i代表右端点,j代表左端点
        while (i > r) del(w[i--], res);  //初始i<j代表没区间
        while (j < l) del(w[j++], res);
        while (j > l) add(w[--j], res);
        ans[id] = res;//记录答案
    }
    for (int i = 0; i < m; i++) {
        printf("%d\n", ans[i]);
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值