抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

题面

洛咕


Solution

题目要求求出区间众数,强制在线。 区间众数是一个比较尴尬的问题,我们无法用区间数据结构来处理这个问题,因为我们没法很好的合并区间众数的答案。 既然区间数据结构解决不了这个问题,我们可以考虑一下使用基于分块的算法,例如莫队。 这题用莫队非常好处理,不幸的是,这题要求强制在线。 因此我们考虑使用分块算法。 分块算法的核心在于把一整个块的信息压缩起来以便快速处理。 我们要查询一段区间的众数,我们可以考虑这样搞:对于这个区间内连续的块,我们先快速地查询这个连续的块中的众数,然后我们暴力处理这个区间剩余的左右两个零散的点,开一个桶暴力维护这些零散的点每个颜色出现的次数,每新加入一个点,就与整个区间的答案比较一下,如果更优就替换答案。 为了实现上面那个思路,我们必须要实现两点:快速求出一段连续块的每个颜色出现的次数,快速求出一段连续块的众数。 对于第一个问题,解决方法很简单,我们暴力做前缀和即可,复杂度$O(n*\sqrt n)$

1
2
3
4
5
6
7
for(int i=1;i<=cnt_block;i++)
{
for(int j=1;j<=to;j++)
pre[i][j]=pre[i-1][j];
for(int j=(i-1)*size;j<i*size;j++)
pre[i][a[j]]++;
}

对于第二个问题,我们可以考虑把每段连续块的答案预处理出来。 具体做法是:我们枚举每个块,然后我们暴力往后扫描,每扫到一个块的结尾就记录答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cnt[0]=-0x3f3f3f3f;
for(int i=1;i<=cnt_block;i++)
{
int t_ans=0;
for(int j=(i-1)*size;j<=n;j++)
{
cnt[a[j]]++;
if(cnt[a[j]]>cnt[t_ans] or (cnt[a[j]]==cnt[t_ans] and a[j]<t_ans))
t_ans=a[j];
if((j+1)%size==0)
f[i][j/size+1]=t_ans;
}
memset(cnt,0,sizeof cnt);
cnt[0]=-0x3f3f3f3f;
}

就酱,这题就被我们搞定啦~ 复杂度$O(n\sqrt n)$


Code

本题实现上有较多细节要处理,请小心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//Luogu P4168 [Violet]蒲公英
//Feb,4th,2019
//分块套路题
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
long long read()
{
long long x=0,f=1; char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int N=40000+1000;
const int M=200+10;
int n,m,a[N],b[N],t_n,mmap[N];//离散值->原值
int f[M][M],pre[M][N];//f[i][j]:块i到j的众数,pre[i][j]:到i块为止,颜色j的出现次数前缀和
int cnt[N];//零时记录每个元素出现次数
int main()
{
//freopen("testdata.in","r",stdin);
//freopen("4168.out","w",stdout);

t_n=n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=read(),b[i]=a[i];

sort(b+1,b+1+n);
int to=0,last=0;
for(int i=1;i<=n;i++)
if(b[i]!=last)
last=b[i],b[++to]=b[i];
for(int i=1;i<=n;i++)
{
int t=lower_bound(b+1,b+1+to,a[i])-b;
mmap[t]=a[i],a[i]=t;
}
int size=int(sqrt(n)),cnt_block=n/size+1;
n=cnt_block*size-1;
for(int i=1;i<=cnt_block;i++)
{
for(int j=1;j<=to;j++)
pre[i][j]=pre[i-1][j];
for(int j=(i-1)*size;j<i*size;j++)
pre[i][a[j]]++;
}
cnt[0]=-0x3f3f3f3f;
for(int i=1;i<=cnt_block;i++)
{
int t_ans=0;
for(int j=(i-1)*size;j<=n;j++)
{
cnt[a[j]]++;
if(cnt[a[j]]>cnt[t_ans] or (cnt[a[j]]==cnt[t_ans] and a[j]<t_ans))
t_ans=a[j];
if((j+1)%size==0)
f[i][j/size+1]=t_ans;
}
memset(cnt,0,sizeof cnt);
cnt[0]=-0x3f3f3f3f;
}

int ans=0;
for(int i=1;i<=m;i++)
{
int l=read(),r=read();
l=(l+ans-1)%t_n+1,r=(r+ans-1)%t_n+1;
if(l>r) swap(l,r);

int bl=l/size+1,br=r/size+1;
ans=0;
if(bl+1<=br-1)
ans=f[bl+1][br-1];
for(int j=l;j<bl*size and j<=r;j++)
{
cnt[a[j]]++;
int tmp1=cnt[a[j]],tmp2=cnt[ans];
if(bl+1<=br-1)
tmp1+=pre[br-1][a[j]]-pre[bl][a[j]],
tmp2+=pre[br-1][ans]-pre[bl][ans];
if(tmp1>tmp2 or (tmp1==tmp2 and a[j]<ans))
ans=a[j];
}
if(bl!=br)
for(int j=(br-1)*size;j<=r;j++)
{
cnt[a[j]]++;
int tmp1=cnt[a[j]],tmp2=cnt[ans];
if(bl+1<=br-1)
tmp1+=pre[br-1][a[j]]-pre[bl][a[j]],
tmp2+=pre[br-1][ans]-pre[bl][ans];
if(tmp1>tmp2 or (tmp1==tmp2 and a[j]<ans))
ans=a[j];
}

for(int j=l;j<bl*size and j<=r;j++)
cnt[a[j]]--;
if(bl!=br)
for(int j=(br-1)*size;j<=r;j++)
cnt[a[j]]--;
cnt[0]=-0x3f3f3f3f;

ans=mmap[ans];
printf("%d\n",ans);
}
return 0;
}

评论