bzoj2724: [Violet 6]蒲公英(分块)

传送门

md调了一个晚上最后发现竟然是空间开小了……明明算出来够的……

讲真其实我以前不太瞧得起分块,觉得这种基于暴力的数据结构一点美感都没有。然而今天做了这道分块的题才发现分块的暴力之美(如果我空间没有开小就更美了)

我们先将整个数组分块,设块的大小为$T$

我们先预处理出所有以块边界为端点的区间的答案,即$ans[L][R]$代表着第$L$块到第$R$块的序列所代表的答案。这个可以$O(n*n/T)$预处理

然后我们先将所有的数给离散化,然后对每一个值都开一个vector,记录这个值在数组中出现的每一个位置。比如数组的下标为1,3,5的位置都是3,那么3的vector记录的就是{1,3,5}

这个有什么用呢?我们设查询的区间为$[l,r]$,然后在这个vector里先二分查找第一个大于等于$l$的数的位置,再二分查找第一个大于$r$的数的位置,那么两个位置一减就是这个数在这个区间中的出现次数。比如查询区间$[2,4]$,我们先找到第一个大于等于2的数3,在vector中下标为2,再找第一个大于4的数为5,下标为3,那么3-2=1就是3这个数字在这个区间中的出现次数

那么,我们设$[L,R]$为查询区间之间的整块,因为我们第一步已经预处理出了所有块与块之间的答案,那么这一段之间的众数也就可以知道。那么,只有区间$[l,L-1]$和$[R+1,r]$之间的数字有可能更新答案。那么我们就去枚举这两个区间中的所有数字,然后用上面说的方法去查询它在整个查询区间内的出现次数,然后更新答案即可

复杂度为$O(n*n/T+n*T*logn)$,设块的大小为$n/sqrt{nlogn}$ ,那么时间复杂度就是$O(nsqrt{nlogn})$

其实还有一种更快的方法是先预处理出块与块之间的答案和各个数的出现次数,然后查询只要在散块里暴力增加并更新答案,之后暴力复原即可(然而我懒并不想打)

然后基本注意点都写在注解里了

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<vector>
 7 #include<cmath>
 8 #define inf 0x3f3f3f3f
 9 using namespace std;
10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
11 char buf[1<<21],*p1=buf,*p2=buf;
12 inline int read(){
13     #define num ch-‘0‘
14     char ch;bool flag=0;int res;
15     while(!isdigit(ch=getc()))
16     (ch==‘-‘)&&(flag=true);
17     for(res=num;isdigit(ch=getc());res=res*10+num);
18     (flag)&&(res=-res);
19     #undef num
20     return res;
21 }
22 char sr[1<<21],z[20];int C=-1,Z;
23 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
24 inline void print(int x){
25     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
26     while(z[++Z]=x%10+48,x/=10);
27     while(sr[++C]=z[Z],--Z);sr[++C]=‘\n‘;
28 }
29 const int N=40005,M=1005;
30 int ans[M][M],a[N],b[N],cnt[N],rt[N],vis[N];
31 vector<int> pos[N];
32 int n,m,q,lastans=0,s,l,r;
33 inline int query_cnt(int x){
34     //查询数的出现次数,注意l和r要开全局变量
35     return upper_bound(pos[x].begin(),pos[x].end(),r)-lower_bound(pos[x].begin(),pos[x].end(),l);
36 }
37 void init(){
38     //暴力枚举块与块之间的答案
39     for(int i=1;i<=rt[n];++i){
40         memset(cnt,0,sizeof(cnt));
41         int bg=s*(i-1)+1,res=a[bg];
42         for(int j=bg;j<=n;++j){
43             ++cnt[a[j]];
44             if(cnt[a[j]]>cnt[res]||(cnt[a[j]]==cnt[res]&&a[j]<res)) res=a[j];
45             ans[i][rt[j]]=res;
46         }
47     }
48 }
49 int query(int l,int r){
50     //查询,小块暴力,大块直接找答案
51     if(rt[r]-rt[l]<=1){
52         int id=0,res=0;
53         for(int i=l;i<=r;++i)
54         if(!vis[a[i]]){
55             int t=query_cnt(a[i]);
56             if(t>res||(t==res&&a[i]<id)) res=t,id=a[i];
57             vis[a[i]]=1;
58         }
59         for(int i=l;i<=r;++i) vis[a[i]]=0;
60         return b[id];
61     }
62     int L=rt[l]+1,R=rt[r]-1;
63     int LL=(L-1)*s+1,RR=R*s;
64     int id=ans[L][R],res=query_cnt(id);vis[id]=1;
65     for(int i=l;i<LL;++i)
66     if(!vis[a[i]]){
67         int t=query_cnt(a[i]);
68         if(t>res||(t==res&&a[i]<id)) res=t,id=a[i];
69         vis[a[i]]=1;
70     }
71     for(int i=RR+1;i<=r;++i)
72     if(!vis[a[i]]){
73         int t=query_cnt(a[i]);
74         if(t>res||(t==res&&a[i]<id)) res=t,id=a[i];
75         vis[a[i]]=1;
76     }
77     for(int i=l;i<LL;++i) vis[a[i]]=0;
78     for(int i=RR+1;i<=r;++i) vis[a[i]]=0;
79     vis[ans[L][R]]=0;
80     return b[id];
81 }
82 int main(){
83     n=read(),q=read(),s=sqrt(n/(double)(log2(n))+1);
84     //我怕s会变成0所以sqrt里加了个1(可能并不需要)
85     for(int i=1;i<=n;++i) a[i]=b[i]=read(),rt[i]=(i-1)/s+1;//分块
86     sort(b+1,b+1+n),m=unique(b+1,b+1+n)-b-1;
87     for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+m,a[i])-b,pos[a[i]].push_back(i);
88     //以上是离散
89     init();
90     while(q--){
91         l=read(),r=read();
92         l=(l+lastans-1)%n+1,r=(r+lastans-1)%n+1;
93         if(l>r) swap(l,r);
94         print(lastans=query(l,r));
95     }
96     Ot();
97     return 0;
98 }

原文地址:https://www.cnblogs.com/bztMinamoto/p/9607299.html

时间: 2024-09-30 06:35:13

bzoj2724: [Violet 6]蒲公英(分块)的相关文章

【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]蒲公英

2724: [Violet 6]蒲公英 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 795  Solved: 248[Submit][Status] Description Input 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 Output Sample Input Sample Output HINT 修正下: n <= 40000, m <= 50000 S

【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的众数位置.具体实现的话直接开个桶存一下就好了. 然后考虑询问,整块的直接拿出来求一下出现次

【分块】bzoj2724 [Violet 6]蒲公英

分块,离散化,预处理出: ①前i块中x出现的次数(差分): ②第i块到第j块中的众数是谁,出现了多少次. 询问的时候,对于整块的部分直接获得答案:对于零散的部分,暴力统计每个数出现的次数,加上差分的结果,尝试更新ans. 1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 int n,m,sum,sz,n

bzoj2724: [Violet 6]蒲公英(离散化+分块)

我好弱啊..这题调了2天QwQ 题目大意:给定一个长度为n(n<=40000)的序列,m(m<=50000)次询问l~r之间出现次数最多的数.(区间众数) 这题如果用主席树就可以不用处理一堆前缀和..蓝鹅我不会~T_T~. 把序列n分成sqrt(n)块,先把所有数离散化,预处理出poi[i][j]表示第i块到第j块的众数(即出现次数最多的数). 询问有两种情况: 第一种情况是l~r在某个块中,那么直接扫一遍求出众数,效率O(sqrt(n)). 第二种情况是l~r在多个块中,l在x块,r在y块,

【BZOJ 2724】 2724: [Violet 6]蒲公英 (区间众数不带修改版本)

2724: [Violet 6]蒲公英 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1908  Solved: 678 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 <= 4

BZOJ 2724: [Violet 6]蒲公英

2724: [Violet 6]蒲公英 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1633  Solved: 563[Submit][Status][Discuss] 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 Outp

【BZOJ 2724】 [Violet 6]蒲公英

2724: [Violet 6]蒲公英 Time Limit: 40 Sec  Memory Limit: 512 MB Submit: 970  Solved: 319 [Submit][Status][Discuss] 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 Out

「Violet」蒲公英

「Violet」蒲公英 传送门 区间众数,强制在线. 分块经典题. 像这题一样预处理,然后就直接爆搞,复杂度 \(O(n \sqrt n)\) 参考代码: #include <algorithm> #include <cstdio> #include <cmath> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x".ou