the way it starts and the way it ends
困难的根号分治题目。
看到众数,肯定要考虑根号分治。不难想到的策略是对着出现次数进行根号分治,分为 \(\ge \sqrt n\) 和 \(< \sqrt n\)。我们把前者称为大数,后者称为小数。
先考虑转化问题,不难考虑为在一个序列上选定一个操作区间,答案就是非操作区间的众数的出现次数+操作区间的众数的出现次数。我们定义 \(x\) 为非操作区间内的众数,\(y\) 为操作区间内的众数。
考虑分讨。
\(x\) 为大数,\(y\) 为小数,考虑转化贡献,为 \(cnt_x - cnt_{x_{in}} + cnt_{y_{in}}\),其中,\(in\) 表示的是在操作区间内的数。因此我们考虑对于后者做最大子段和,具体的,对于每一个 \(x\),枚举 \(y\),显然只有 \(y\) 的出现点为关键点,\(x\) 可以做前缀和,复杂度 \(O(n \sqrt n\))。
\(x\) 为小数,\(y\) 为大数,同样考虑转化贡献,为 \(cnt_x + cnt_{y_{in}} - cnt_{x_{in}}\)。因此我们同样可以枚举 \(x\),显然也是可以以 \(x\) 的出现位置作为关键点,做最大子段和的。两部分是类似的。
\(x\) 为小数,\(y\) 为小数,我们不妨枚举 \(x\),一个关键的引理是操作的区间是贴着 \(x\) 的出现位置的,否则我们可以扩大操作区间,显然不会影响到 \(x\) 在区间外的出现次数,而区间内的众数出现次数是只可能扩大的。所以这样的区间个数是 \(\sum {cnt_i}^2\) 级别的,而 \(cnt \le B\),故原式 \(\le n \cdot B\)。因此我们证明了区间个数是根号级别的。问题转化为如何快速求出一个区间内的众数。
考虑 \(y\) 也是小数,我们不妨从此入手,获得众数为某个特定值的存在区间。进一步的,我们设计 \(f[k][i]\),表示从 \(i\) 出发的区间,众数大小为 \(k\) 的最小的 \(r\)。这个是可以对于每一个 \(k\) 双指针解决的。然后再考虑查询,不难发现对于序列的每一个位置 \(i\),我们至多只会有根号次查询以 \(i\) 为左端点,因此也是可以双指针的。具体的,一个指针对应的是 \(r\),这个根据上面的分析是单调递增的,另一个指针对应的是 \(k\),显然随着 \(r\) 的增加,是不降的。
所以,总体的时间复杂度是 \(O(n \sqrt n)\)。
Code
不要忘记考虑边界了,也就是第一个 \(pos\) 之前和最后一个 \(pos\) 之后。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 2e5 + 10,K = 1000;
vector<int> pos[N];
int a[N],lsh[N];
int sum[N];
int cnt[N];
int f[K][N];
int ban[N];
vector<int> ans_pos;
void solve(){
int n;
cin >> n;
for(int i=1;i<=n;i++){
cin >> a[i];
lsh[i] = a[i];
}
sort(lsh+1,lsh+1+n);
int len = unique(lsh+1,lsh+1+n) - lsh - 1;
for(int i=1;i<=n;i++){
a[i] = lower_bound(lsh+1,lsh+1+len,a[i]) - lsh;
pos[a[i]].push_back(i);
}
int B = sqrt(n) + 1;
for(int i=1;i<=B;i++)
fill(f[i]+1,f[i]+1+n,0);
fill(ban+1,ban+1+len,0);
int ans = 0;
for(int i=1;i<=len;i++){
int res = 0;
if(pos[i].size() >= B){
sum[0] = 0;
ban[i] = 1;
res = max(res,(int)pos[i].size());
for(int j=1;j<=n;j++)
sum[j] = sum[j-1] + (a[j] == i);
// x not op
for(int j=1;j<=len;j++){
int pre,nw,mx;
pre = nw = mx = 0;
if(j == i) continue;
for(int v : pos[j]){
nw -= sum[v] - sum[pre];
if(nw < 0) nw = 0;
nw ++ ;
mx = max(mx,nw);
pre = v;
}
res = max(res,mx + (int)pos[i].size());
}
// x is op,y is not op
if(res > ans){
ans = res;
ans_pos.clear();
ans_pos.push_back(i);
}
else if(ans == res)
ans_pos.push_back(i);
for(int j=1;j<=len;j++){
int pre = 0,mx = 0,nw = 0;
if(j == i) continue;
for(int v : pos[j]){
nw += sum[v] - sum[pre];
mx = max(mx,nw);
nw -- ;
if(nw < 0) nw = 0;
pre = v;
}
nw += sum[n] - sum[pre];
mx = max(mx,nw);
if(nw < 0) nw = 0;
if(mx + (int)(pos[j].size()) > ans){
ans = mx + (int)(pos[j].size());
ans_pos.clear();
ans_pos.push_back(j);
}
else if(mx + (int)(pos[j].size()) == ans)
ans_pos.push_back(j);
}
}
}
// x < B,y < B
for(int k=1;k<=B;k++){
int L = 1,R = 0;
fill(cnt+1,cnt+1+len,0);
int fl = 0;
while(R <= n){
if(!fl && R == n){
while(L <= n){
f[k][L] = n + 1;
L ++ ;
}
break;
}
if(!fl && R < n){
R ++ ;
if(!ban[a[R]]){
cnt[a[R]] ++ ;
if(cnt[a[R]] == k) fl ++ ;
}
}
else{
if(!ban[a[L]]){
if(cnt[a[L]] == k) fl -- ;
cnt[a[L]] -- ;
}
f[k][L] = R;
L ++ ;
}
}
}
for(int i=1;i<=len;i++){
int res = 0;
if(pos[i].size() >= B) continue;
res = max(res,(int)(pos[i].size()));
for(int j=0;j<pos[i].size();j++){
int k = 0;
for(int l=j+1;l<pos[i].size();l++){
int x = pos[i][j],y = pos[i][l];
if(x == y - 1) continue;
while(k <= B && f[k][x+1] < y) k ++ ;
k -- ;
res = max(res,(int)(pos[i].size() - (l - j - 1) + k));
}
if(pos[i][j] < n){
while(k <= B && f[k][pos[i][j]+1] <= n) k ++ ;
k -- ;
res = max(res,j + 1 + k);
}
}
if(pos[i][0] > 1){
int k = 0;
for(int l=0;l<pos[i].size();l++){
int x = 0,y = pos[i][l];
while(k <= B && f[k][x+1] < y) k ++ ;
k -- ;
res = max(res,(int)(pos[i].size() - l + k));
}
}
if(ans < res){
ans = res;
ans_pos.clear();
ans_pos.push_back(i);
}
else if(ans == res)
ans_pos.push_back(i);
}
cout << ans << "\n";
sort(ans_pos.begin(),ans_pos.end());
ans_pos.erase(unique(ans_pos.begin(),ans_pos.end()),ans_pos.end());
for(int x : ans_pos)
cout << lsh[x] << "\n";
ans_pos.clear();
for(int i=1;i<=len;i++)
pos[i].clear();
}
int main(){
IOS;
int T;
cin >> T;
while(T -- )
solve();
return 0;
}

浙公网安备 33010602011771号