zoukankan      html  css  js  c++  java
  • 题解 P4688 [Ynoi2016]掉进兔子洞

    这是我的第一道 Ynoi,同时也一发卡到了最优解,发篇题解纪念一下 qwq,不开 $O_2$ 也是能过的。

    本题需要的知识点:

    - 分块,莫队
    - $ ext{bitset}$ 优化
    - 离散化
    - 其它的奇技淫巧

    首先明确这题要求的,三个区间内不同时出现的数的个数。列出柿子就是 $(r_1 - l_1 + 1) + (r_2 - l_2 + 1) + (r_3 - l_3 + 1) - rep$,其中 $rep$ 是三个区间内同时出现的数的个数。

    首先 $1 le a_i le 10^9$,显然离散化。同时注意,本题离散化时不能去重,即不需要 $ ext{unique}$,直接使用 $ ext{lower} ext{_bound}$ 即可。

    随后对每个询问求删除的数,对于这种对多个区间内元素个数的维护我们不难想到莫队,如莫队板子题 小 Z 的袜子 就是这种题型。

    老套路,将长度为 $n$ 的序列拆成 $sqrt n$ 块,把所有询问的区间以 $l$ 为第一关键字,$r$ 为第二关键字排序。

    此时考虑如何求出上文的 $rep$。对每个询问开一个长度为 $n$ 的 $ ext{bitset}$,每一位是否为 $1$ 即表示这一位是否被删除。再维护另一个 $ ext{bitset}$ $Bst$,每一位是否为 $1$ 表示这一区间是否有这个数。则最后要删除的数就是三个区间的 $Bst$ 取交集。

    最后要注意,

    - 此题 $1 le n, m le 10^5$,开不下这么大的 $ ext{bitset}$,需要分几次求答案。

    - 区间移动位置时,要先加再减。如果先减,则可能会让 $cnt$ 变成负数再变成正数,会导致 $ ext{bitset}$ 无效访问内存,从而导致 RE。

    于是这题就结束了,$ ext{Code}$:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <bitset>
      4 #include <cstring>
      5 #include <cmath>
      6 #include <algorithm>
      7 #define il inline
      8 #define rg register
      9 
     10 //namespace IO {
     11 //    const int SIZE = (1 << 20) + 1;
     12 //    char ibuf[SIZE], *iS, *iT, obuf[SIZE],*oS = obuf, *oT = obuf + SIZE - 1;
     13 //    char _st[55];
     14 //    int _qr = 0;
     15 //    inline char gc() {
     16 //        return (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS++) : *iS++);
     17 //    }
     18 //    inline void qread() {}
     19 //    template<class T1, class ...T2>
     20 //    inline void qread(T1 &IEE, T2&... ls) {
     21 //        register T1 __ = 0, ___ = 1;
     22 //        register char ch;
     23 //        while(!isdigit(ch = gc())) ___ = (ch == '-') ? -___ : ___;
     24 //        do {
     25 //            __ = (__ << 1) + (__ << 3) + (ch ^ 48);
     26 //        }while(isdigit(ch = gc()));
     27 //        __ *= ___;
     28 //        IEE = __;
     29 //        qread(ls...);
     30 //        return ;
     31 //    }
     32 //    inline void flush() {
     33 //        fwrite(obuf, 1, oS - obuf, stdout);
     34 //        oS = obuf;
     35 //        return ;
     36 //    }
     37 //    inline void putc_(char _x) {
     38 //        *oS++ = _x;
     39 //        if(oS == oT) flush();
     40 //    }
     41 //    inline void qwrite() {}
     42 //    template<class T1, class ...T2>
     43 //    inline void qwrite(T1 IEE, T2... ls) {
     44 //        if(!IEE) putc_('0');
     45 //        if(IEE < 0) putc_('-'), IEE = -IEE;
     46 //        while(IEE) _st[++_qr] = IEE % 10 + '0', IEE /= 10;
     47 //        while(_qr) putc_(_st[_qr--]);
     48 //        qwrite(ls...);
     49 //        return ;
     50 //    }
     51 //    struct Flusher_{~Flusher_(){flush();}}io_flusher;
     52 //}
     53 //
     54 //using namespace IO;
     55 //此部分是快读,为防止抄袭注释掉了。
     56 //注 : 此快读板子是@SPFA(uid=177999) 给我的,为防止他被说是 ctj 特来此声明一下。
     57 using namespace std;
     58 
     59 const int N = 1e5 + 5;
     60 const int M = 25005;
     61 
     62 bitset<N> bst[M], Bst;//bitset 开 25000,分四次求出答案。
     63 bool vis[M];
     64 int n, m, tot, blocksize;
     65 int a[N], b[N], blocknum[N];
     66 int ans[N], cnt[N];
     67 
     68 struct Node {
     69     int id, l, r;
     70     bool operator < (const Node &oth) const {
     71         return blocknum[l] == blocknum[oth.l] ? r < oth.r : blocknum[l] < blocknum[oth.l];
     72     }
     73 } Que[N << 2];
     74 
     75 il void Add(int id) {
     76     int x = a[id];
     77     ++cnt[x];
     78     Bst[x + cnt[x] - 1] = 1;     
     79 }
     80 
     81 il void Del(int id) {
     82     int x = a[id];
     83     --cnt[x];
     84     Bst[x + cnt[x]] = 0;
     85 }
     86 
     87 il void Solve(int k) {
     88     tot = 0;
     89     memset(vis, false, sizeof vis);
     90     memset(ans, 0, sizeof ans);
     91     memset(cnt, 0, sizeof cnt);
     92     //因为是分次求出答案,不要忘记每次初始化。
     93     for(rg int i = 1; i <= k; i++) {
     94         for(int j = 1; j <= 3; j++) {
     95             Que[++tot].id = i;
     96             qread(Que[tot].l, Que[tot].r);
     97             ans[i] += Que[tot].r - Que[tot].l + 1;
     98         }
     99     }
    100     sort(Que + 1, Que + tot + 1);
    101     Bst.reset();
    102     int l = 1, r = 0;
    103     for(rg int i = 1; i <= tot; i++) {
    104         while(l > Que[i].l) Add(--l);
    105         while(r < Que[i].r) Add(++r);
    106         while(l < Que[i].l) Del(l++);
    107         while(r > Que[i].r) Del(r--);
    108           //区间移动位置先加再减。
    109         if(!vis[Que[i].id]) bst[Que[i].id] = Bst, vis[Que[i].id] = true;//此区间未访问过,直接赋值
    110         else bst[Que[i].id] &= Bst;//访问过,取交集
    111     }
    112     for(rg int i = 1; i <= k; i++) ans[i] -= bst[i].count() * 3;
    113     for(rg int i = 1; i <= k; i++) printf("%d
    ", ans[i]);
    114 }
    115 
    116 int main() {
    117     qread(n, m);
    118     blocksize = sqrt(n);
    119     for(rg int i = 1; i <= n; i++) {
    120         qread(a[i]);
    121         b[i] = a[i];
    122         blocknum[i] = (i - 1) / blocksize + 1;
    123     }
    124     sort(b + 1, b + n + 1);
    125     for(rg int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;//离散化,不需要去重
    126     int T = M - 5;
    127     while(m) {
    128         if(m < T) {
    129             Solve(m);
    130             break;
    131         }
    132         else Solve(T), m -= T;
    133     }//分次求出答案
    134     return 0;
    135 }
    View Code
  • 相关阅读:
    对象的创建
    Java运行时数据区域
    Java内存模型
    LinkedList小练习及相关算法
    面试题之矩阵与转置矩阵相乘
    快速排序
    垃圾收集器
    java垃圾收集相关问题
    Win7下安装Centos7双系统出错:No valid bootloader target device found.
    Scanner类的方法
  • 原文地址:https://www.cnblogs.com/BreezeEnder/p/13893495.html
Copyright © 2011-2022 走看看