[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;
}