题解:P11398 众数
D. 众数 官方题解
本题涉及的主要知识点:
- 【3】递推法
- 【4】倍增法
测试点 1\sim 9
要算出众数,关键就是要统计每个数字出现的次数。1 操作正常修改(直接增加
知道前
对于测试点
测试点 10\sim 15
当
当
本节中这句话以下的内容并不是 J 组需要掌握的,也不是做出这题所必需的。
当 multiset 等,代价是往前挪一步的时间复杂度变成
因此本题的总时间复杂度为
测试点
测试点 16\sim 19
(这是本题最具有提示性的部分分)
注意到
正解
现在
答案是肯定的。注意到
具体要怎么做呢?考虑如下流程:
- 先假设
k\le 1 ,根据递推起点(即前n-1 个数组的统计结果)把后1 个权值算出来,统计答案。 - 若
k\le 1 时无解,就假设k\le 2 ,根据递推起点(即前n-2 个数组的统计结果)把后2 个权值算出来,统计答案。 - 还是不行就假设
k\le 4 ,算出后4 个数的权值,以此类推。
因为现在有
本节中这句话以下的内容并不是 J 组需要掌握的,也不是做出这题所必需的。
如果你学过分块,你或许会发现(因为我没亲自写过),似乎不用倍增法设置起点也能把这题做出来。
具体地,如果你每
我自己用根号一段的方法没有 A 掉本题,我不太清楚选手有没有根号干过去的。
#include<bits/stdc++.h>
using namespace std;
int T,n,m;
int a[300005],b[300005];
vector<int> stt;
long long cnt[20][300005],best[20],v[300005];
bool x_gr_y(int x,int y,int j){return cnt[j][x]>cnt[j][y] || (cnt[j][x]==cnt[j][y] && x>y);}
int main(){
scanf("%d%d%d",&T,&n,&m);
for(int j=0;(1<<j)<=n;j++)stt.emplace_back(n+1-(1<<j));
stt.emplace_back(1);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
for(int j=0;j<stt.size();j++)
for(int i=1;i<stt[j];i++){
cnt[j][b[i]]+=a[i];
if(x_gr_y(b[i],best[j],j))
best[j]=b[i];
}
for(;m;m--){
int op,x,y;
scanf("%d",&op);
if(op==1){
scanf("%d%d",&x,&y);
a[x]+=y;
for(int j=0;j<stt.size();j++)
if(x<stt[j]){
cnt[j][b[x]]+=y;
if(x_gr_y(b[x],best[j],j))
best[j]=b[x];
}
}
else{
long long q;
scanf("%lld",&q);
int last=n+1,answer=-1;
for(int j=0;j<stt.size();j++){
long long bst=best[j];
for(int i=stt[j];i<last;i++){
cnt[j][b[i]]+=a[i];
if(x_gr_y(b[i],bst,j))
bst=b[i];
v[i]=bst*a[i];
}
for(int i=last-1;i>=stt[j];i--){
q^=v[i];
if(q==0 && answer<0)answer=i;
cnt[j][b[i]]-=a[i];
}
if(answer>=0){
printf("%d\n",n+1-answer);
break;
}
else
last=stt[j];
}
}
}
return 0;
}
Further More
出这道题,主要就是发现考纲里有个“倍增法”,然后回顾了脑海当中所有关于“倍增”的内容。
本题最开始是想丢到基础赛的,所以出题受到知识点的限制,能维护的量非常有限,我枚举了好几天才找到了符合需求的一些操作。
在成功出出来这道题之前,我曾经有过很多个不同尝试,下面列举一些:
- 排列
p ,1 操作交换,2 操作维护最大值(操作可逆) - 1 操作单点修改,2 操作前缀按位或(维护每个 bit 的出现次数,仍然可逆)
- 受【模板】回滚莫队 的启发,权值为当前
a_i 位置减第一次出现位置(但是模板自己也被普通莫队做出来了) - 2 操作为前缀 LIS(无法从中间修改导致可以用半可持久化数组实现可逆)
- 2 操作为 P3203(然而加入和删除同一个复杂度)
- 1 操作邻项交换,2 操作前缀逆序对数(J 组不能出现树状数组)
- 值域
10^9 ,1 操作单点修改,2 操作维护最大值(希望最大的一个,但是线段树不仅能做,而且遍历线段树均摊O(1) ,故放弃) - 1 操作临时单点修改,2 操作前缀最大子段和(理由同上)
- 询问群友,群友给出来一个区间凸包(别说 J 组了,我自己都不会做)
- 群友还给出来吉司机线段树(因为是均摊复杂度,所以直接上可持久化复杂度是错的)(J 组更不能出现)
- 1 操作单点插入,2 操作求前缀众数(这会导致倍增起点变化进而被卡,而且需要树状数组维护下标,超纲了)
总结:线段树,太强大。