*题解:P8496 [NOI2022] 众数
解析
对于操作 1、2、4,显然可以使用链表来维护。
考虑操作 3 怎么做。根据题意,我们并不关心数字的排列顺序。所以不妨在维护链表的同时维护一个表示每个数字出现次数的桶,最后只需将对应链表的桶合并然后求最大值。直接合并是 \(O(n^2)\) 的,但是可以分治(这样想会比较自然吗?),设当前考虑的区间为 \([l,r]\),如果求出位于 \([l,mid]\) 中数字的出现次数和与位于 \([mid + 1,r]\) 中数字的出现次数和,就可以根据求出的值往众数可能所在的那半边走。求值可以通过每个序列维护一个线段树来实现。这样向下单侧递归 \(O(\log n)\) 次,每一层查询 \(O(m)\) 次。总复杂度 \(O(C_m \log^2 n)\)。但是发现查询的区间正好完整对应上了线段树上的区间,所以可以在往下走的同时维护每棵线段树当前区间对应结点编号。这样复杂度就变为 \(O(C_m \log n)\)。
代码
注意链表的一些操作要判空,我这里写的是自创构式写法。
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
const int N = 5e5 + 5,M = 1e6 + 5,L = 20;
int val[M * L],ls[M * L],rs[M * L],rt[M],head[M],tail[M],w[M],nxt[M],pre[M],sz[M],lcnt,cnt;
void merge(int x,int y,int z){
if(sz[x] == 0){
head[x] = head[y];
tail[x] = tail[y];
sz[x] = sz[y];
}else if(sz[x] > 0 && sz[y] > 0){
nxt[tail[x]] = head[y];
pre[head[y]] = tail[x];
tail[x] = tail[y];
sz[x] += sz[y];
}
sz[z] = sz[x];
head[z] = head[x];
tail[z] = tail[x];
}
void push_back(int x,int y){
lcnt++;
if(!head[x]) tail[x] = head[x] = lcnt;
else{
nxt[tail[x]] = lcnt;
pre[lcnt] = tail[x];
tail[x] = lcnt;
}
w[lcnt] = y;
sz[x]++;
}
void pop_back(int x){
tail[x] = pre[tail[x]];
nxt[tail[x]] = 0;
sz[x]--;
if(!sz[x]) head[x] = 0;
}
int back(int x){
return w[tail[x]];
}
void push_up(int p){
val[p] = val[ls[p]] + val[rs[p]];
}
void add(int &p,int l,int r,int k,int x){
if(l > k || r < k) return;
if(!p) p = ++cnt;
if(l == r){
val[p] += x;
return;
}
add(ls[p],l,mid,k,x),add(rs[p],mid + 1,r,k,x);
push_up(p);
}
int merge(int x,int y,int l,int r){
if(!x || !y){
return x | y;
}
if(l == r){
val[x] += val[y];
return x;
}
ls[x] = merge(ls[x],ls[y],l,mid);
rs[x] = merge(rs[x],rs[y],mid + 1,r);
push_up(x);
return x;
}
int ask(int l,int r,ll siz,vector<int> &v){
if(l == r) return l - 1;
ll lsum = 0,rsum = 0;
for(int i=0;i<v.size();i++){
lsum += val[ls[v[i]]];
rsum += val[rs[v[i]]];
}
if(lsum * 2 > siz){
for(int i=0;i<v.size();i++){
v[i] = ls[v[i]];
}
return ask(l,mid,siz,v);
}
if(rsum * 2 > siz){
for(int i=0;i<v.size();i++){
v[i] = rs[v[i]];
}
return ask(mid + 1,r,siz,v);
}
return -1;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++){
int l;
cin>>l;
for(int j=1;j<=l;j++){
int x;
cin>>x;
x++;
// cerr<<j<<'\n';
add(rt[i],1,M,x,1);
push_back(i,x);
}
}
while(q--){
int op;
cin>>op;
if(op == 1){
int x,y;
cin>>x>>y;
push_back(x,y);
y++;
add(rt[x],1,M,y,1);
}else if(op == 2){
int x;
cin>>x;
add(rt[x],1,M,back(x),-1);
pop_back(x);
}else if(op == 3){
int m;
cin>>m;
vector<int> v;
ll sum = 0;
for(int i=1;i<=m;i++){
int x;
cin>>x;
v.push_back(rt[x]);
sum += sz[x];
}
cout<<ask(1,M,sum,v)<<'\n';
}else{
int x1,x2,x3;
cin>>x1>>x2>>x3;
rt[x3] = merge(rt[x1],rt[x2],1,M);
merge(x1,x2,x3);
}
}
return 0;
}

浙公网安备 33010602011771号