zoukankan      html  css  js  c++  java
  • 【卡常 bitset 分块】loj#6499. 「雅礼集训 2018 Day2」颜色

     好不容易算着块大小,裸的分块才能过随机极限数据;然而这题在线的数据都竟然是构造的……

    题目描述

    有 $n$ 个数字,第 $i$ 个数字为 $a_i$。

    有 $m$ 次询问,每次给出 $k_i$ 个区间,每个区间表示第 $l_{i, j}$ 到 $r_{i, j}$ 的数字,求这些区间中一共出现了多少种不同的数字。

    部分数据强制在线。

    第一行包含三个整数 $n, m, p$,$p$ 为 $0$ 或 $1$ 表示是否强制在线。

    第二行 $n$ 个正整数,第 $i$ 个表示 $a_i$。

    接下来依次给出每个询问,每个询问第一行一个正整数,表示 $k_i$,接下来 $k_i$ 行,每行两个正整数,分别表示 $l_{i, j}$ 和 $r_{i, j}$,若 $p = 1$ 且这不是第一个询问,输入的 $l_{i, j}$ 和 $r_{i, j}$ 是经过加密的,你需要将这两个数字分别异或上上一个询问的答案,对 $n$ 取模后再加 $1$,两者较小值为真实的 $l_{i, j}$,较大值为真实的 $r_{i, j}$。

    对于全部数据,$1 leq n, m, sum k_i, a_i leq 10^5, 1 leq l_{i, j} leq r_{i, j} leq n$。

    • 子任务 $ m 1(points:10)$:$n, m, sum k_i, a_i leq 5000$
    • 子任务 $ m 2(points:10)$:$n, m, leq 5000$
    • 子任务 $ m 3(points:20)$:$k_i = 1$
    • 子任务 $ m 4(points:20)$: $p = 0$
    • 子任务 $ m 5(points:20)$:$1 leq n, m, sum k_i, a_i leq 50000$
    • 子任务 $ m 6(points:20)$:无特殊限制

    题目分析

    首先会有一个朴素的分块想法:将序列分块,每个块维护一个bitset。

    但是我们会很快意识到bitset一次或操作的复杂度是$O(值域)$的,这么大的复杂度显然我们无法接受。这个想法的最大瓶颈在于中间连续一段bitset的操作复杂度太高。

    用以维护分块处理这个瓶颈的方法有两种:线段树/ST表,也就是用基础的区间数据结构来维护一段区间的bitset值。

    让我们冷静分析一下这两种方法的复杂度:

    我们很容易想到用`bitset`做,先不管空间限制的话会想到这两种方法:

    • 1. 线段树:构造 $O({ n^2overomega })​$ ,询问 $O({ n^2overomega }log_2n)​$ 。
    • 2. ST表:构造 $O({ n^2overomega }log_2n)$ ,询问 $O({ n^2overomega })$ 。

    都是无法承受的,于是考虑分块来降低复杂度,只记录块之间的信息:

    • 1. 线段树:构造 $O({ n^2overomega S })$ ,询问 $O({ n^2overomega }log_2{ nover S }+nS)$ 。
    • 2. ST表:构造 $O({ n^2overomega S }log_2{ nover S })$ ,询问 $O({ n^2overomega }+nS)$ 。

    发现线段树的那个 $log_2{nover S}$ 搞不掉,于是gg了,而ST表 $S$ 设置的正常点复杂度都没什么问题。

    不过这道题卡内存,所以 $S$ 需要给大点。

    这是一个非常规分块的技巧,好像叫四毛子算法?

    ——摘自ZZK的题解

    那么,只需要手写bitset+卡常+分块就可以解决这题了。

     1 #include<bits/stdc++.h>
     2 const int maxn = 100013;
     3 const int maxp = (100000>>5)+3;
     4 const int maxt = (1<<16)-1;
     5 const int maxb = 73;
     6 
     7 int a[maxn],dt[maxn],lg2[maxn],t[maxn],tot;
     8 struct bitset
     9 {
    10     unsigned int a[maxp];
    11     bitset operator |(bitset b) const
    12     {
    13         bitset c;
    14         for (int i=0; i<=tot; i++)
    15             c.a[i] = a[i]|b.a[i];
    16         return c;
    17     }
    18     void reset()
    19     {
    20         for (int i=0; i<=tot; i++) a[i] = 0;
    21     }
    22     void set(int x)
    23     {
    24         a[x>>5] |= 1u<<(x&31);
    25     }
    26     int count()
    27     {
    28         int ret = 0;
    29         for (int i=0; i<=tot; i++)
    30             ret += dt[a[i]>>16]+dt[a[i]&maxt];
    31         return ret;
    32     }
    33 }f[maxb][9],ans;
    34 int n,m,p,lastans;
    35 int blk[maxn],size,bkTot;
    36 
    37 int read()
    38 {
    39     char ch = getchar();
    40     int num = 0, fl = 1;
    41     for (; !isdigit(ch); ch=getchar())
    42         if (ch=='-') fl = -1;
    43     for (; isdigit(ch); ch=getchar())
    44         num = (num<<1)+(num<<3)+ch-48;
    45     return num*fl;
    46 }
    47 void init()
    48 {
    49     for (int i=1, Mx=65535; i<=Mx; i++)
    50         for (int x=i; x; x&=(x-1)) ++dt[i];
    51     for (int i=2, Mx=100000; i<=Mx; i++)
    52         lg2[i] = lg2[i>>1]+1;
    53 }
    54 void deal(int l, int r)
    55 {
    56     int ls = blk[l], rs = blk[r];
    57     if (ls==rs){
    58         for (int i=l; i<=r; i++) ans.set(a[i]);
    59         return;
    60     }
    61     for (int i=l; blk[i]==ls; i++) ans.set(a[i]);
    62     for (int i=r; blk[i]==rs; i--) ans.set(a[i]);
    63     if (ls+1 < rs){
    64         int t = lg2[rs-ls-1];
    65         ans = ans|f[ls+1][t]|f[rs-(1<<t)][t];
    66     }
    67 }
    68 int main()
    69 {
    70     n = read(), m = read(), p = read();
    71     init(), size = sqrt(1.2*n*log2(n+1)), bkTot = n/size+1;
    72     for (int i=1; i<=n; i++)
    73         t[i] = a[i] = read(), blk[i] = i/size+1;
    74     std::sort(t+1, t+n+1);
    75     tot = std::unique(t+1, t+n+1)-t-1;
    76     for (int i=1; i<=n; i++)
    77         a[i] = std::lower_bound(t+1, t+tot+1, a[i])-t,
    78         f[blk[i]][0].set(a[i]);
    79     tot >>= 5;
    80     for (int j=1; j<=lg2[bkTot]; j++)
    81         for (int i=1; i+(1<<j)-1<=bkTot; i++)
    82             f[i][j] = f[i][j-1]|f[i+(1<<(j-1))][j-1];
    83     for (int tim=0; m; --m, ++tim)
    84     {
    85         ans.reset();
    86         for (int k=read(); k; --k)
    87         {
    88             int l = read(), r = read();
    89             if (p&&tim) l = (l^lastans)%n+1, r = (r^lastans)%n+1;
    90             if (l > r) std::swap(l, r);
    91             deal(l, r);
    92         }
    93         lastans = ans.count();
    94         printf("%d
    ",lastans);
    95     }
    96     return 0;
    97 }

    END

  • 相关阅读:
    leetcode每日一题:836. 矩形重叠
    单链表之删除头结点,查找等于定值x的结点数,单链表的逆置
    拼数,零幺串
    最大公约数/最小公倍数
    寻找二叉树双亲结点
    Object类的派生-c++
    牛客小白月赛22
    二叉树的基本操作
    字符串的反转,替换,删除
    [2011山东ACM省赛] Identifiers(模拟)
  • 原文地址:https://www.cnblogs.com/antiquality/p/10392422.html
Copyright © 2011-2022 走看看