莫队+分块 BZOJ 3809

3809: Gty的二逼妹子序列

Time Limit: 80 Sec  Memory Limit: 28 MB
Submit: 1634  Solved: 482
[Submit][Status][Discuss]

Description

Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题。

对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数。

为了方便,我们规定妹子们的美丽度全都在[1,n]中。

给定一个长度为n(1<=n<=100000)的正整数序列s(1<=si<=n),对于m(1<=m<=1000000)次询问“l,r,a,b”,每次输出sl...sr中,权值∈[a,b]的权值的种类数。

Input

第一行包括两个整数n,m(1<=n<=100000,1<=m<=1000000),表示数列s中的元素数和询问数。

第二行包括n个整数s1...sn(1<=si<=n)。

接下来m行,每行包括4个整数l,r,a,b(1<=l<=r<=n,1<=a<=b<=n),意义见题目描述。

保证涉及的所有数在C++的int内。

保证输入合法。

Output

对每个询问,单独输出一行,表示sl...sr中权值∈[a,b]的权值的种类数。

Sample Input

10 10

4 4 5 1 4 1 5 1 2 1

5 9 1 2

3 4 7 9

4 4 2 5

2 3 4 7

5 10 4 4

3 9 1 1

1 4 5 9

8 9 3 3

2 2 1 6

8 9 1 4

Sample Output

2

0

0

2

1

1

1

0

1

2

HINT

样例的部分解释:

5 9 1 2

子序列为4 1 5 1 2

在[1,2]里的权值有1,1,2,有2种,因此答案为2。

3 4 7 9

子序列为5 1

在[7,9]里的权值有5,有1种,因此答案为1。

4 4 2 5

子序列为1

没有权值在[2,5]中的,因此答案为0。

2 3 4 7

子序列为4 5

权值在[4,7]中的有4,5,因此答案为2。

建议使用输入/输出优化。

= =,我之前看卿学姐的代码,思考着他的莫队+分块是怎么做的,想着想着就发现暂时不清楚怎么分块,于是打算自己尝试写写看莫队+线段树来更新,然后果然TLE,因为这样的复杂度是O(n^1.5 * logn)

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 1e5 + 5;
const int maxm = 1000000 + 5;
int block, pos[maxn], sz, s[maxn];
struct Point{
    int l, r, ca, cb, id;
}q[maxm];
int res[maxm];
bool cmp(Point a, Point b){
    if(pos[a.l]==pos[b.l])
        return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
int n, m;
struct Sedment_tree{
    int colorcnt, colornum;
}tree[maxn << 2];

inline void push_up(int o){
    int lb = o << 1, rb = o << 1 | 1;
    tree[o].colornum = tree[lb].colornum + tree[rb].colornum;
}

void update(int p, int l, int r, int o, int val){
    if (p == l && p == r){
        tree[o].colorcnt += val;
        if (tree[o].colorcnt == 0) tree[o].colornum = 0;
        if (tree[o].colorcnt == 1) tree[o].colornum = 1;
        return ;
    }
    int mid = (l + r) / 2;
    if (p <= mid) update(p, l, mid, o << 1, val);
    if (p > mid) update(p, mid + 1, r, o << 1 | 1, val);
    push_up(o);
}

void add(int x){
    update(s[x], 1, n, 1, 1);
}

void del(int x){
    update(s[x], 1, n, 1, -1);
}

int query(int ql, int qr, int l, int r, int o){
    int ans = 0;
    if (ql <= l && qr >= r){
        ans += tree[o].colornum; return ans;
    }
    int mid = (l + r) / 2;
    if (ql <= mid) ans += query(ql, qr, l, mid, o << 1);
    if (qr > mid) ans += query(ql, qr, mid + 1, r, o << 1 | 1);
    return ans;
}

int main(){
    n = read(), m = read();
    sz = sqrt(n);
    for (int i = 1; i <= n; i++){
        s[i] = read();
        pos[i] = (i - 1) / sz;
    }
    for (int i = 1; i <= m; i++){
        q[i].l = read(), q[i].r = read(), q[i].ca = read(), q[i].cb = read();
        q[i].id = i;
    }
    sort(q + 1, q + 1 + m, cmp);
    int L = 1, R = 0;
    for (int i = 1; i <= m; i++){
        while (L > q[i].l){
            L--; add(L);
        }
        while (R < q[i].r){
            R++; add(R);
        }
        while (L < q[i].l){
            L++; del(L - 1);
        }
        while (R > q[i].r){
            R--; del(R + 1);
        }
        res[q[i].id] = query(q[i].ca, q[i].cb, 1, n, 1);
    }
    for (int i = 1; i <= m; i++){
        printf("%d\n", res[i]);
    }
    return 0;
}

因此改为莫队+分块吧

然后思考了一下,我们对于询问区间进行莫队离线操作即可。然后对于修改操作,我们就利用分块来控制颜色区间即可。

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 1e5 + 5;
const int maxm = 1000000 + 5;
int block, pos[maxn], sz, belong[maxn], num, l[maxn], r[maxn], cnt[maxn];
///pos是莫队算法,表示目前这个询问区间是哪个块的
///belong是分块,表示目前这个颜色是属于哪个块的
int s[maxn];
struct Point{
    int l, r, ca, cb, id;
}q[maxm];
int res[maxm], color[maxn];
bool cmp(Point a, Point b){
    if(pos[a.l]==pos[b.l])
        return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
int n, m;

void build(){
    block = sqrt(n); num = n / block;
    if (n % block) num++;
    for (int i = 1; i <= num; i++){
        l[i] = (i - 1) * block + 1; r[i] = i * block;
    }
    r[num] = n;
    for (int i = 1; i <= n; i++){
        belong[i] = (i - 1) / block + 1;
    }
}

void add(int x){
    color[x]++;
    if(color[x] == 1) cnt[belong[x]]++;
}

void del(int x){
    color[x]--;
    if(color[x] == 0) cnt[belong[x]]--;
}

int query(int x, int y){
    int ans = 0;
    if (belong[x] == belong[y]){
        for (int i = x; i <= y; i++) if (color[i]) ans++;
        return ans;
    }
    for (int i = x; i <= r[belong[x]]; i++){
        if (color[i]) ans++;
    }
    for (int i = l[belong[y]]; i <= y; i++){
        if (color[i]) ans++;
    }
    for (int i = belong[x] + 1; i < belong[y]; i++)
        ans += cnt[i];
    return ans;
}

int main(){
    n = read(), m = read();
    sz = sqrt(n);
    for (int i = 1; i <= n; i++){
        s[i] = read();
        pos[i] = (i - 1) / sz;
    }
    for (int i = 1; i <= m; i++){
        q[i].l = read(), q[i].r = read(), q[i].ca = read(), q[i].cb = read();
        q[i].id = i;
    }
    sort(q + 1, q + 1 + m, cmp);
    int L = 1, R = 0;
    build();
    for (int i = 1; i <= m; i++){
        while (L > q[i].l){
            L--; add(s[L]);
        }
        while (R < q[i].r){
            R++; add(s[R]);
        }
        while (L < q[i].l){
            L++; del(s[L - 1]);
        }
        while (R > q[i].r){
            R--; del(s[R + 1]);
        }
        res[q[i].id] = query(q[i].ca, q[i].cb);
    }
    for (int i = 1; i <= m; i++){
        printf("%d\n", res[i]);
    }
    return 0;
}

关键:深刻了解莫队算法和分块

时间: 2024-10-06 17:36:53

莫队+分块 BZOJ 3809的相关文章

[BZOJ 3585] mex 【莫队+分块】

题目链接:BZOJ - 3585 题目分析 区间mex,即区间中没有出现的最小自然数. 那么我们使用一种莫队+分块的做法,使用莫队维护当前区间的每个数字的出现次数. 然后求mex用分块,将权值分块(显然mex 一定小于等于 n ,大于 n 的权值没有意义,可以直接忽略),每块大小 sqrt(n) . 然后区间中的某个数的数量被减到0的时候就将它所在的块的种类计数减一,添加数的时候类似. 然后枚举每个块,找到最小的中间有数不存在的块(即种类数小于块中的数的种数),然后到这个快里直接从小一个一个找到

BZOJ 4129 Haruna’s Breakfast 带修改树上莫队+分块

题目大意:给定一棵树,每个点有一个非负点权,支持下列操作 1.修改某个点的点权 2.查询某条链上的mex 考虑链上不带修改的版本,我们可以用莫队+分块来搞(链接戳这里) 现在到了树上带修改,果断糖果公园 本来抱着逗比的心态写了一发结果1.4s过了 跟糖果公园的80s完全不成正比啊0.0 #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <

HDU 5145 NPY and girls (莫队分块离线)

题目地址:HDU 5145 莫队真的好神奇..这样的复杂度居然只有n*sqrt(n)... 裸的莫队分块,先离线,然后按左端点分块,按块数作为第一关键字排序,然后按r值作为第二关键字进行排序.都是从小到大,可以证明这样的复杂度只有n*sqrt(n).然后进行块之间的转移. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <

[BZOJ 3236] [Ahoi2013] 作业 &amp;&amp; [BZOJ 3809] 【莫队 | 分块】

题目链接: BZOJ - 3236   BZOJ - 3809 算法一:莫队 首先,单纯的莫队算法是很好想的,就是用普通的第一关键字为 l 所在块,第二关键字为 r 的莫队. 这样每次端点移动添加或删除一个数字,用树状数组维护所求的信息就是很容易的.由于这里有 logn复杂度,所以复杂度还是挺高的. 于是 BZOJ-3236 的时限 100s,我的代码跑了 98s,险过...... However..BZOJ-3809 的出题人(SLYZ的神犇)就没有这么善良了!直接内存限制 28MB 就直接把

【BZOJ 3809】 3809: Gty的二逼妹子序列 (莫队+分块)

3809: Gty的二逼妹子序列 Time Limit: 80 Sec  Memory Limit: 28 MBSubmit: 1728  Solved: 513 Description Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题. 对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数. 为了方便,我们规定妹子们的美丽度全都在[1,n]中. 给定一个长度为n(1<=n<=100000)的正整数序列s(1<=si<=n),对

Bzoj 3809: Gty的二逼妹子序列 莫队,分块

3809: Gty的二逼妹子序列 Time Limit: 35 Sec  Memory Limit: 28 MBSubmit: 868  Solved: 234[Submit][Status][Discuss] Description Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题. 对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数. 为了方便,我们规定妹子们的美丽度全都在[1,n]中. 给定一个长度为n(1<=n<=100000)的

bzoj 3809 Gty的二逼妹子序列 —— 莫队+分块

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3809 据说一开始应该想到莫队+树状数组,然而我想的却是莫队+权值线段树... 如果用权值线段树,则修改和查询都是 O(logn),总复杂度 O(n√nlogn),艰难...(而且仔细一看空间有点卡?) 看了TJ,才发现权值也可以分块,则查询 O(√n) 但修改 O(1),就可以过咯~ 代码如下: #include<iostream> #include<cstdio> #inc

bzoj3920: Yuuna的礼物(莫队+分块套分块)

思路挺简单的,但是总感觉好难写...码力还是差劲,最后写出来也挺丑的 这题显然是个莫队题,考虑怎么转移和询问... 根据莫队修改多查询少的特点,一般用修改快查询慢的分块来维护.查第$k_1$小的出现次数可以用权值分块做到$O(1)$修改,$O(\sqrt{n})$查询,$k_2$小的数同理.对于每一种出现次数$i$,预处理出有几种数在序列里的出现次数$\geq i$,并在每种出现次数中对这些数离散化,这样我们就能对每种出现次数进行权值分块查第$k_2$小的数了. 因为$\sum cnt_i=n$

BZOJ_3585_mex &amp;&amp; BZOJ_3339_Rmq Problem_莫队+分块

Description 有一个长度为n的数组{a1,a2,...,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问l,r. Output 一行一个数,表示每个询问的答案. Sample Input 5 5 2 1 0 2 1 3 3 2 3 2 4 1 2 3 5 Sample Output 1 2 3 0 3 HINT 数据规模和约定 对于100%的数据: 1<=n,m<=200000 0<=ai&l