【bzoj2724】蒲公英(分块)

题目分析

付费题哈哈。题意就是求区间众数,由于区间众数无法快速合并,所以不能使用传统的数据结构如线段树等。

这时分块就能派上很大的用场。将序列分成$\sqrt{n}+$块,每块大小$\sqrt{n}+$,通过预处理得到cnt[i][j], ans[i][j]分别表示i在前j块中出现的次数,和第i块到第j块的众数是多少。

那么查询时我们就得到了至多$\sqrt{n}$个连续的块,和至多$2\sqrt{n}$个零散的元素,对于零散的元素,暴力,对于连续的块,直接使用预处理。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;

const int N = 4e4 + 5, oo = 0x7fffffff;
int n, m, S;
int val[N], num[N], len;
int blk[N], bl[300], br[300], blkCnt, sum[N];
int ans[300][300], cnt[N][300];

int read(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<‘0‘||ch>‘9‘)&&ch!=‘-‘;ch=getchar());
    if(ch==‘-‘) {f=-1;ch=getchar();}
    for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) i=(i<<3)+(i<<1)+(ch^48);
    return f*i;
}

inline void wr(int x){
    if(x < 0) putchar(‘-‘), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + ‘0‘);
}

inline void disc_init(){
    sort(num + 1, num + len + 1);
    len = unique(num + 1, num + len + 1) - (num + 1);
    for(int i = 1; i <= n; i++)
        val[i] = lower_bound(num + 1, num + len + 1, val[i]) - num;
//  for(int i = 1; i <= n; i++) cout<<val[i]<<endl;
}

inline void initBlk(){
    blk[1] = 1, bl[blkCnt = 1] = 1, cnt[val[1]][1] = 1;
    for(int i = 2; i <= n; i++){
        if(i % S == 0){
            br[blkCnt] = i;
            blk[i] = blkCnt;
            cnt[val[i]][blkCnt]++;
            if(i + 1 <= n)
                bl[++blkCnt] = i + 1;
            continue;
        }
        blk[i] = blkCnt;
        cnt[val[i]][blkCnt]++;
    }
    br[blkCnt] = n;
}

inline void initData(){
    for(int i = 1; i <= len; i++)
        for(int j = 2; j <= blkCnt; j++)
            cnt[i][j] += cnt[i][j - 1];
    for(int i = 1; i <= blkCnt; i++){
        int maxx = -oo, cur = oo, sum[N] = {0};
        for(int j = i; j <= blkCnt; j++){
            for(int k = bl[j]; k <= br[j]; k++){
                int ret = ++sum[val[k]];
                if(ret > maxx) maxx = ret, cur = val[k];
                else if(ret == maxx && val[k] < cur)
                    cur = val[k];
            }
            ans[i][j] = cur;
        }
    }
}

inline int query(int l, int r){
    if(l > r) swap(l, r);
    int blk_L = blk[l] + 1, blk_R = blk[r] - 1;
    if(bl[blk_L - 1] == l) blk_L--;
    if(br[blk_R + 1] == r) blk_R++;
    if(blk_L > blk_R){
        int sum[N] = {0}, maxx = -oo, ret = oo;
        for(int i = l; i <= r; i++){
            int c = ++sum[val[i]];
            if(c > maxx) maxx = c, ret = val[i];
            else if(c == maxx && val[i] < ret) ret = val[i];
        }
        return ret;
    }
    int ret = ans[blk_L][blk_R], sum[N] = {0}, ret_cnt = cnt[ret][blk_R] - cnt[ret][blk_L - 1];
    for(int i = l; i < bl[blk_L]; i++){
        int c;
        if(sum[val[i]]) c = ++sum[val[i]];
        else{
            sum[val[i]] = cnt[val[i]][blk_R] - cnt[val[i]][blk_L - 1];
            c = ++sum[val[i]];
        }
        if(c > ret_cnt) ret = val[i], ret_cnt = c;
        else if(c == ret_cnt && val[i] < ret)
            ret = val[i];
    }
    for(int i = br[blk_R] + 1; i <= r; i++){
        int c;
        if(sum[val[i]]) c = ++sum[val[i]];
        else{
            sum[val[i]] = cnt[val[i]][blk_R] - cnt[val[i]][blk_L - 1];
            c = ++sum[val[i]];
        }
        if(c > ret_cnt) ret = val[i], ret_cnt = c;
        else if(c == ret_cnt && val[i] < ret)
            ret = val[i];
    }
    return ret;
}

int main(){
    //freopen("h.in","r",stdin);
    n = read(), m = read(); S = 400;
    for(int i = 1; i <= n; i++) val[i] = num[++len] = read();
    disc_init();
    initBlk();
    initData();
    int ans = 0;
    for(int i = 1; i <= m; i++){
        int l = read(), r = read();
        l = (l + ans - 1) % n + 1, r = (r + ans - 1) % n + 1;
        wr(ans = num[query(l, r)]), putchar(‘\n‘);
    }
    return 0;
}
时间: 2024-11-06 07:18:21

【bzoj2724】蒲公英(分块)的相关文章

[日常摸鱼]bzoj2724蒲公英-分块

区间众数经典题~ http://begin.lydsy.com/JudgeOnline/problem.php?id=4839这里可以提交~ 题意大概就是没有修改的询问区间众数,如果有一样的输出最小的,强制在线,$n \leq 4*10^4,a_i \leq 10^9$. log数据结构脑补一遍好像没什么可以做的,数据范围我们可以分块! 不过分块之前肯定要离散化一下,而且还要保存离散化前的数据(因为要回答的是出现最多的数),离散化的方法在上一篇博客里面~ 假设分成$L$块,每块大小$s=\lfl

【BZOJ2724】[Violet 6]蒲公英 分块+二分

[BZOJ2724][Violet 6]蒲公英 Description Input 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 Output Sample Input 6 3 1 2 3 2 1 2 1 5 3 6 1 5 Sample Output 1 2 1 HINT 修正下: n <= 40000, m <= 50000 题解:分块还是练脑子啊~ 结论:一个区间的众数要么是区间中一个块的众数,要么是块外的任意

【bzoj2724】[Violet 6]蒲公英 分块+STL-vector

题目描述 输入 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 输出 样例输入 6 3 1 2 3 2 1 2 1 5 3 6 1 5 样例输出 1 2 1 题解 分块+STL-vector 一个显而易见的结论:区间众数一定是一段连续的块的众数或块外的数,证明略(逃 先把数据离散化,然后分块预处理出f[i][j],表示从块i到块j的众数位置.具体实现的话直接开个桶存一下就好了. 然后考虑询问,整块的直接拿出来求一下出现次

[Luogu P4168] [Violet]蒲公英 (分块)

题面 洛咕 Solution 题目要求求出区间众数,强制在线. 区间众数是一个比较尴尬的问题,我们无法用区间数据结构来处理这个问题,因为我们没法很好的合并区间众数的答案. 既然区间数据结构解决不了这个问题,我们可以考虑一下使用基于分块的算法,例如莫队. 这题用莫队非常好处理,不幸的是,这题要求强制在线. 因此我们考虑使用分块算法. 分块算法的核心在于把一整个块的信息压缩起来以便快速处理. 我们要查询一段区间的众数,我们可以考虑这样搞:对于这个区间内连续的块,我们先快速地查询这个连续的块中的众数,

[bzoj2724]蒲公英

分块,可以发现众数一定是整块的众数或在不整块中出现的数,预处理出f[i][j]表示第i块到第j块的众数,然后对于询问暴力枚举所有散块的数,相当于要支持查询一个数在一个区间内出现的次数,可以用可持久化权值线段树,也可以直接对每一个数开一个vector记录位置二分(离散),时间复杂度是$o(nKlog_{2}n+n^{2}/K)$,取$K=sqrt(n/log_{2}n)$即可 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define K

[Violet]蒲公英 分块

Code: #include<cstdio> #include<string> #include<iostream> #include<algorithm> #include<vector> #include<cstring> #include<cmath> using namespace std; void setIO(string a){ freopen((a+".in").c_str(),&quo

P4168 [Violet]蒲公英 分块

这道题算是好好写了.写了三种方法. 有一个好像是$qwq$$N\sqrt(N)$的方法,,但是恳请大佬们帮我看看为什么这么慢$qwq$(后面的第三种) 注:$pos[i]$表示$i$属于第$pos[i]$块. 第一种是统计所有可能的块组成的区间中(第i块到第j块),每个数出现的次数,记做$f[i][j][k]$,和所有可能的块组成的区间的答案,记做$h[i][j]$. 然后每次先把整块的答案作为初始答案,然后对于散块中的每个值$vl$,暴力修改对应的$f[i][j][vl]$,更新答案. 当块长

部分系列题

当然是不齐的. JZP系列 JZPKIL       数论,反演,积性函数,伯努利数,(常数优化) JZPFAR     k-d树 JZPTAB    分块 hash sam[太可怕了 不会写] JZPLCM    三维偏序,可持久化线段树维护 JZPEXT     数位统计(常数优化) JZPGYZ     suffix array水过 JZPCIR      找规律(OEIS水过) JZPFOR  三维哈密尔顿回路统计[太可怕了 不会] JZPLIT   构造 JZPLIT2  优化异或方程

BZOJ 刷题记录 PART 5

拖了好久才写的. [BZOJ2821]接触分块大法.这道题略有点新颖.首先我们先分块,然后统计每块中每个数出现的个数. 下面是联立各个方块,预处理出第I个方块到第J个方块出现正偶数次数的个数. for (i=1;i<=s;i++) { for (j=i;j<=s;j++) { sum[i][j]=sum[i][j-1]; for (k=a[j].l;k<=a[j].r;k++) { temp[data[k]]++; if (!(temp[data[k]]&1)) sum[i][j

Noip前的大抱佛脚----根号对数算法

根号算法 分块 数列分块入门九题(hzwer) 入门题1,2,3,4,5,7 问题:给一段区间打上标记后单点查询 解法:主要是每块维护一些标记,计算答案等,此类分块较为简单 注意:块大小一般为\(\sqrt n\) 复杂度:\(O(n\sqrt n)\) 入门题6 问题:每次朝数列中间插入一个元素,查询第k个元素是什么 解法:块大小超过一定值后暴力重构!采用链表实现 复杂度:\(O(n\sqrt n)\) 入门题8 问题:每次询问一个区间内为\(c?\)的元素个数,并把整个区间改为\(c?\)