*题解: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;
}
posted @ 2026-05-12 19:02  yutar  阅读(11)  评论(0)    收藏  举报