[NOI2022] 众数

传送门
考场上没有想到什么摩尔排序之类的做法,感觉很多稍微思维一点的,就是遇到过才会,没遇到过就是想不到。
摩尔排序:求题目中个数严格大于总个数一半的众数
发现用这个是可以区间合并的:维护\((val,cnt)\)。即最后剩下来的那个数的值和个数。
考场上想到了启发式合并的deque+线段树合并(操作三也直接硬合并多棵树,所以是伪的)。
deque可以用链表(手写链式前向星代替),当然deque要简单粗暴的多,链表细节会多一些,调了好几发才过。
总结一下:线段树(操作4需要线段树合并)维护\((val,cnt)\),每个序列的值用链表维护。
deque空间不需要\(10^6\),只需要\(5*10^5\)就可以了,它也是能过的(不过慢三倍):

  • deque
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 3;
const int M = N * 21;

char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int rd() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	return x*f;
}



deque<int> Q[N >> 1];
int mp[N], rt[N], cnt[M], val[M], ls[M], rs[M], nd;

void P_up(int x) {
	int L(ls[x]), R(rs[x]);
	if(val[L] == val[R]) cnt[x] = cnt[L] + cnt[R];
	else {
		val[x] = (cnt[L] > cnt[R]) ? val[L] : val[R];
		cnt[x] = abs(cnt[L] - cnt[R]);
	}
}

void Update(int &x, int l, int r, int p, int d) {
	if(!x) x = ++nd;
	if(l == r) {val[x] = l; cnt[x] += d; return;}
	int mid = (l + r) >> 1;
	(p <= mid) ? Update(ls[x], l, mid, p, d) : Update(rs[x], mid + 1, r, p, d);
	P_up(x);
}

int Merge(int x, int y) {
	if(!x || !y) return x + y;
	if(!ls[x] && !rs[x]) {cnt[x] += cnt[y]; return x;}
	ls[x] = Merge(ls[x], ls[y]);
	rs[x] = Merge(rs[x], rs[y]);
	P_up(x);
	return x;
}

int Query(int x, int l, int r, int p) {
	if(l == r) return cnt[x];
	int mid = (l + r) >> 1;
	return (p <= mid) ? Query(ls[x], l, mid, p) : Query(rs[x], mid + 1, r, p);
}

int st[N];
int main() {
//	freopen("major13.in", "r", stdin);
//	freopen("rt.out", "w", stdout);
	int n = rd(), q = rd(), up;
	up = n + q;
	for(int i = 1; i <= n; i++) {
		int l = rd();
		mp[i] = i;
		for(int j = 1; j <= l; j++) {
			int x = rd();
			Q[i].push_back(x);
			Update(rt[i], 1, up, x, 1);
//			printf("!rt=%d\n",rt[i]); 
		}
	}
//	printf("!%d\n", nd);
	while(q--) {
		int op = rd();
		printf("%d\n", op);
		if(op == 1) {
			int x = rd(), y = rd();
			x = mp[x];
			Q[x].push_back(y);
			Update(rt[x], 1, up, y, 1);
		}
		else if(op == 2) {
			int x = rd();
			x = mp[x];
			int y = Q[x].back(); Q[x].pop_back();
			Update(rt[x], 1, up, y, -1);
		}
		else if(op == 3) {
			int m = rd();
			int tot = 0, v = -1; ll c = 0;
			for(int i = 1; i <= m; i++) {
				int x = rd();
				x = mp[x]; st[i] = x;
				int rx = rt[x];
				if(v == val[rx]) {c += cnt[rx];}
				else {
					if(cnt[rx] >= c) {v = val[rx]; c = cnt[rx] - c;}
					else {c -= cnt[rx];}
				}
				tot += Q[x].size();
			}
			if(v < 1) {printf("-1\n"); continue;}
			c = 0;
			for(int i = 1; i <= m; i++) {c += Query(rt[st[i]], 1, up, v);}
//			printf("!v = %d: c = %d\n", v, c);
			tot >>= 1; 
			if(c <= tot) printf("-1\n");
			else printf("%d\n", v);
		}
		else {
			int x = rd(), y = rd(), z = rd();
			//Merge x into y
			x = mp[x]; y = mp[y];
			if(Q[x].size() <= Q[y].size()) {
				while(!Q[x].empty()) {
					int v = Q[x].back(); Q[x].pop_back();
					Q[y].push_front(v);
				}
				rt[y] = Merge(rt[y], rt[x]);
				mp[z] = y;
			}
			else {
				while(!Q[y].empty()) {
					int v = Q[y].front(); Q[y].pop_front();
					Q[x].push_back(v);
				}
				rt[x] = Merge(rt[x], rt[y]);
				mp[z] = x;
			}
		}
	}
//	printf("!%d\n", nd);
	return 0;
}
  • 链表
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 3;
const int M = N * 21;

char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int rd() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	return x*f;
}

int mp[N], rt[N], cnt[M], val[M], ls[M], rs[M], nd;

void P_up(int x) {
	int L(ls[x]), R(rs[x]);
	if(val[L] == val[R]) cnt[x] = cnt[L] + cnt[R];
	else {
		if(cnt[L] < cnt[R]) {val[x] = val[R]; cnt[x] = cnt[R] - cnt[L];}
		else {val[x] = val[L]; cnt[x] = cnt[L] - cnt[R];}
	}
}

void Update(int &x, int l, int r, int p, int d) {
	if(!x) x = ++nd;
	if(l == r) {val[x] = l; cnt[x] += d; return;}
	int mid = (l + r) >> 1;
	(p <= mid) ? Update(ls[x], l, mid, p, d) : Update(rs[x], mid + 1, r, p, d);
	P_up(x);
}

int Merge(int x, int y) {
	if(!x || !y) return x + y;
	if(!ls[x] && !rs[x]) {cnt[x] += cnt[y]; return x;}
	ls[x] = Merge(ls[x], ls[y]);
	rs[x] = Merge(rs[x], rs[y]);
	P_up(x);
	return x;
}

int Query(int x, int l, int r, int p) {
	if(!x) return 0;
	if(l == r) return cnt[x];
	int mid = (l + r) >> 1;
	return (p <= mid) ? Query(ls[x], l, mid, p) : Query(rs[x], mid + 1, r, p);
}

int head[N], tl[N], ecnt, to[N], pre[N], sz[N];
void Add(int i, int x) {
	++ecnt;
	pre[ecnt] = tl[i]; tl[i] = ecnt; to[ecnt] = x;
	if(!head[i]) head[i] = ecnt, pre[ecnt] = 0;
}

int st[N];
int main() {
//	freopen("major.in", "r", stdin);
//	freopen("major.out", "w", stdout);
	int n = rd(), q = rd(), up;
	up = n + q;
	for(int i = 1; i <= n; i++) {
		int l = rd();
		mp[i] = i; sz[i] = l;
		for(int j = 1; j <= l; j++) {
			int x = rd();
			Update(rt[i], 1, up, x, 1);
			Add(i, x);
//			printf("!rt=%d\n",rt[i]); 
		}
	}
//	printf("!%d\n", nd);
	while(q--) {
		int op = rd();
		if(op == 1) {
			int x = rd(), y = rd();
			x = mp[x];
			Update(rt[x], 1, up, y, 1);
			Add(x, y);
			sz[x]++;
		}
		else if(op == 2) {
			int x = rd();
			x = mp[x];
			int y = to[tl[x]];
			tl[x] = pre[tl[x]];
			Update(rt[x], 1, up, y, -1);
			if(!--sz[x]) {tl[x] = head[x] = 0;}
		}
		else if(op == 3) {
			int m = rd();
			int tot = 0, v = -1; ll c = 0;
			for(int i = 1; i <= m; i++) {
				int x = rd();
				x = mp[x]; st[i] = x;
				if(!sz[x]) continue;
				int rx = rt[x];
				if(v == val[rx]) {c += cnt[rx];}
				else {
					if(cnt[rx] >= c) {v = val[rx]; c = cnt[rx] - c;}
					else {c -= cnt[rx];}
				}
				tot += sz[x];
			}
			if(v < 1) {printf("-1\n"); continue;}
			c = 0;
			for(int i = 1; i <= m; i++) {c += Query(rt[st[i]], 1, up, v);}
			tot >>= 1;
			if(c <= tot) printf("-1\n");
			else printf("%d\n", v);
		}
		else {
			int x = rd(), y = rd(), z = rd();
			x = mp[x]; y = mp[y];
			if(!sz[x]) head[x] = head[y], tl[x] = tl[y];
			else if(sz[y]) pre[head[y]] = tl[x], tl[x] = tl[y];
			sz[x] += sz[y];
			rt[x] = Merge(rt[x], rt[y]);
			mp[z] = x;
		}
	}
//	printf("!%d\n", nd);
	return 0;
}