ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

洛谷P4688 掉进兔子洞 题解

2022-04-05 20:34:17  阅读:166  来源: 互联网

标签:10 洛谷 int 题解 询问 bitset 区间 include P4688


题面

传送门
给定一个长度为 \(N\) 的序列 \(a\) 以及 \(M\) 次询问,每次询问为三个区间 \([l_1,r_1],[l_2,r_2],[l_3,r_3]\)。把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和。
注意这里删掉指的是一个一个删,不是把等于这个值的数直接删完,比如三个区间是 \([1,2,2,3,3,3,3]\),\([1,2,2,3,3,3,3]\) 与 \([1,1,2,3,3]\),就一起扔掉了 \(1\) 个 \(1\),\(1\) 个 \(2\),\(2\) 个 \(3\)。
\(1≤N,M≤10^5\),\(1≤a_i≤10^9\)。

题解

与普通莫队不同,这题每个询问为三个区间。那么我们就将所有区间分开处理,每次将区间的信息记录下来,最后统一更新每个询问的答案。但是我们发现,这题无法再用一个变量存储区间的信息,而需要用一个可以存储所有数值信息的数据结构。
先考虑一种较为暴力的解法:用数组记录,下标为数值,记录出现的次数。加入和删除均可以 \(O(1)\) 实现。对于每个询问的区间,将数组复制并记录,最后统一处理。
但数组自然不行,因为无法快速将实时迭代的信息转移到每个询问的区间上,并且空间也无法承受。于是想到使用bitset
然而bitset有其弊端,就是只能记录0/1两种状态,而无法实现本题要求的“每个数出现了几次”。这里需要使用一种奇淫技巧:
设总共有 \(S\) 个数,那么bitset原要开 \(S\) 位;但我们开 \(N\) 位,并给予每一位一个二元组 \((x,y)\),表示数 \(x\) 是否出现了至少 \(y\) 次。显然 \(N\) 位可以表示出所有的状态。在这一题中,由于数字本来就要离散化,预处理很好实现。
再来看莫队的增减操作,显然还是可以 \(O(1)\) 实现。
最后统一处理所有区间,容易想到只需将三个区间做与运算,结果为 \(1\) 的位数就是需要删去的位数。那么这一步也可以进一步简化(主要是减少空间),在求每个区间答案时用迭代的bitset实时更新。
这个做法对空间需求很大,需要开 \(10^5\) 个长度为 \(10^5\) 的bitset。估算为1200M,而本题空间限制为500M。可以将所有询问分成三次做,时间复杂度少有变化,空间可以减小为三分之一。

Code

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<bitset>
using namespace std;
const int N = 100010, M = 33340;
int n, a[N], mp[N], pos[N], len[M], cnt[N];
bitset<N> ans[M], res;
struct Query {
    int id, l, r;
    bool operator <(const Query &oth) const {
        return pos[l] != pos[oth.l] ? pos[l] < pos[oth.l] : r < oth.r;
    }
} q[N];
int read() {
    int x = 0; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}
    return x;
}
void add(int x) {
    res[x + cnt[x]] = 1; cnt[x]++;
}
void del(int x) {
    cnt[x]--; res[x + cnt[x]] = 0;
}
void Solve(int m) {
    for (int i = 1; i <= m; i++) {
        int l1 = read(), r1 = read(), l2 = read(), r2 = read(), l3 = read(), r3 = read();
        q[3 * i - 2] = (Query){i, l1, r1};
        q[3 * i - 1] = (Query){i, l2, r2};
        q[3 * i] = (Query){i, l3, r3};
        len[i] = r1 + r2 + r3 - l1 - l2 - l3 + 3;
    }
    sort(q + 1, q + 3 * m + 1);
    for (int i = 1; i <= m; i++) ans[i].set();
    memset(cnt, 0, sizeof (cnt)); res.reset();
    for (int i = 1, L = 1, R = 0; i <= 3 * m; i++) {
        while (R < q[i].r) add(a[++R]);
        while (R > q[i].r) del(a[R--]);
        while (L < q[i].l) del(a[L++]);
        while (L > q[i].l) add(a[--L]);
        ans[q[i].id] &= res;
    }
    for (int i = 1; i <= m; i++) printf("%d\n", len[i] - ans[i].count() * 3);
}
int main() {
    n = read(); int m = read();
    for (int i = 1; i <= n; i++) mp[i] = a[i] = read();
    sort(mp + 1, mp + n + 1);
    for (int i = 1; i <= n; i++) a[i] = lower_bound(mp + 1, mp + n + 1, a[i]) - mp;
    int len = max(1, (int)sqrt((double)n * n / m));
    for (int i = 1; i <= n; i++) pos[i] = (i - 1) / len + 1;
    Solve(m / 3); Solve(m / 3); Solve(m - m / 3 * 2);
    return 0;
}

标签:10,洛谷,int,题解,询问,bitset,区间,include,P4688
来源: https://www.cnblogs.com/Fisher-Y/p/16103701.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有