20260516 - 部分分训练 1

目前分数:65pts

Joler老师分数:80pts

这是一道 NOI 题,所以有很多部分分。

老师想让我们在死磕的基础上拿到更多的分。

题目描述

对于一个序列,定义其众数为序列中出现次数严格大于一半的数字。注意该定义与一般的定义有出入,在本题中请以题面中给出的定义为准。

一开始给定 \(n\) 个长度不一的正整数序列,编号为 \(1 \sim n\),初始序列可以为空。这 \(n\) 个序列被视为存在,其他编号对应的序列视为不存在。

\(q\) 次操作,操作有以下类型:

  • \(1 \ x \ y\):在 \(x\) 号序列末尾插入数字 \(y\)。保证 \(x\) 号序列存在,且 \(1 \le x, y \le n + q\)
  • \(2 \ x\):删除 \(x\) 号序列末尾的数字,保证 \(x\) 号序列存在、非空,且 \(1 \le x \le n + q\)
  • \(3 \ m \ x_1 \ x_2 \dots \ x_m\):将 \(x_1, x_2, \ldots, x_m\) 号序列顺次拼接,得到一个新序列,并询问其众数。如果不存在满足上述条件的数,则返回 \(-1\)。数据保证对于任意 \(1 \le i \le m\)\(x_i\) 是一个仍然存在的序列,\(1 \le x_i \le n + q\),且拼接得到的序列非空。注意:不保证 \(\boldsymbol{x_1, \ldots, x_m}\) 互不相同,询问中的合并操作不会对后续操作产生影响。
  • \(4 \ x_1 \ x_2 \ x_3\):新建一个编号为 \(x_3\) 的序列,其为 \(x_1\) 号序列后顺次添加 \(x_2\) 号序列中数字得到的结果,然后删除 \(x_1, x_2\) 对应的序列。此时序列 \(x_3\) 视为存在,而序列 \(x_1, x_2\) 被视为不存在,在后续操作中也不会被再次使用。保证 \(1 \le x_1, x_2, x_3 \le n + q\)\(x_1 \ne x_2\)、序列 \(x_1, x_2\) 在操作前存在、且在操作前没有序列使用过编号 \(x_3\)

数据范围

对于所有测试数据,保证 \(1 \le n, q, C_m, C_l \le 5 \times {10}^5\)

\(n, q\) \(C_m, C_l\) 测试点编号 特殊性质 A 特殊性质 B 特殊性质 C
\(\le 300\) \(\le 300\) \(1 \sim 3\)
\(\le 4000\) \(\le 4000\) \(4 \sim 7\)
\(\le {10}^5\) \(\le {10}^5\) \(8\)
\(\le {10}^5\) \(\le {10}^5\) \(9\)
\(\le {10}^5\) \(\le {10}^5\) \(10\)
\(\le {10}^5\) \(\le {10}^5\) \(11 \sim 12\)
\(\le {10}^5\) \(\le {10}^5\) \(13\)
\(\le 5 \times {10}^5\) \(\le 5 \times {10}^5\) \(14\)
\(\le 5 \times {10}^5\) \(\le 5 \times {10}^5\) \(15\)
\(\le 5 \times {10}^5\) \(\le 5 \times {10}^5\) \(16\)
\(\le 5 \times {10}^5\) \(\le 5 \times {10}^5\) \(17 \sim 18\)
\(\le 5 \times {10}^5\) \(\le 5 \times {10}^5\) \(19 \sim 20\)

特殊性质 A:保证 \(n = 1\) 且没有操作 \(4\)
特殊性质 B:保证任意时刻任何序列中只有数字 \(1\)\(2\)
特殊性质 C:保证没有操作 \(2\)

部分分分析

显然,要离线询问。

暴力

显然,按照题目模拟。

{
    // BL
    array<int, 10010> cnt{}; 
    for (auto qy : qry) {
        int op = qy[0];
        if (op == 1) {
            int x = qy[1], y = qy[2];
            a[x].push_back(y);
        } else if (op == 2) {
            int x = qy[1];
            a[x].pop_back();
        } else if (op == 3) {
            int sumSZ = 0;
            for (int j = 2; j < (int)qy.size(); j++) {
                int x = qy[j];
                sumSZ += a[x].size();
                for (auto k : a[x]) {
                    ++cnt[k];
                }
            }
            int pos = max_element(cnt.begin(), cnt.end()) - cnt.begin();
            if (cnt[pos] * 2 > sumSZ) printf("%d\n", pos);
            else puts("-1");
            for (int j = 2; j < (int)qy.size(); j++) {
                int x = qy[j];
                for (auto k : a[x]) {
                    --cnt[k];
                }
            }
        } else {
            int x1 = qy[1], x2 = qy[2], x3 = qy[3];
            a[x3].swap(a[x1]);
            for (auto v : a[x2]) a[x3].push_back(v);
        }
    }
}

性质 B

因为只有两种数,所以开两个桶去算。

但是会 TLE,因为暴力合并会 T 飞。

所以要启发式合并,顺序的问题用 deque 就可以了。

事实证明不用开 long longlist

if (markB) {
    vector<array<int, 3>> cnt(n + q + 1, array<int, 3>{});
    for (int i = 1; i <= n; i++) {
        for (auto j : a[i]) {
            ++cnt[i][j];
        }
    }
    for (auto qy : qry) {
        int op = qy[0];
        if (op == 1) {
            int x = qy[1], y = qy[2];
            ++cnt[x][y];
            a[x].push_back(y);
        } else if (op == 2) {
            int x = qy[1];
            --cnt[x][a[x].back()];
            a[x].pop_back();
        } else if (op == 3) {
            ll l1 = 0, l2 = 0;
            for (int j = 2; j < (int)qy.size(); j++) {
                int x = qy[j];
                l1 += cnt[x][1];
                l2 += cnt[x][2];
            }
            if (l1 > l2) puts("1");
            else if (l2 > l1) puts("2");
            else puts("-1");
        } else {
            int x1 = qy[1], x2 = qy[2], x3 = qy[3];
            if (a[x1].size() > a[x2].size()) {
                a[x3].swap(a[x1]);
                cnt[x3].swap(cnt[x1]);
                for (auto v : a[x2]) a[x3].push_back(v);
                cnt[x3][1] += cnt[x2][1];
                cnt[x3][2] += cnt[x2][2];
            } else {
                a[x3].swap(a[x2]);
                cnt[x3].swap(cnt[x2]);
                for (auto v : a[x1]) a[x3].push_front(v);
                cnt[x3][1] += cnt[x1][1];
                cnt[x3][2] += cnt[x1][2];
            }
        }   
    }
    return 0;
}

性质 A

因为只有一个组数,所以可以开一个线段树记录最大值。

if (n == 1 && markA) {
    const int maxl = 1e6;
    for (int i = 1; i <= n; i++) {
        for (auto j : a[i]) {
            change(1, 1, maxl, j, 1);
        }
    }
    for (auto qy : qry) {
        int op = qy[0];
        if (op == 1) {
            int x = qy[1], y = qy[2];
            change(1, 1, maxl, y, 1);
            a[x].push_back(y);
        } else if (op == 2) {
            int x = qy[1];
            change(1, 1, maxl, a[x].back(), -1);
            a[x].pop_back();
        } else if (op == 3) {
            auto ans = query(1, 1, maxl, 1, maxl);
            if (ans.maxv * 2 > (int)a[1].size())
                printf("%d\n", ans.id);
            else
                puts("-1");
        } else {
            assert(0);
        }
    }
    return 0;
}

性质 C

还没写出来。

就是用一个叫做摩尔投票的东西。

模板在这里

然后用一些奇奇妙妙的方法合并。

然后就没有然后了。

PS:好像可以线段树的时候维护。

65pts 代码:

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define ull unsigned long long
#define db double
#define inf (1 << 30)
#define lnf (1LL << 60)
#define all(x) (x).begin(), (x).end()
typedef pair<int, int> PII;
constexpr int N = 5e5 + 7, Q = 5e5 + 7;
constexpr int P = 998244353;

int n, q;

struct Info {
	int maxv, id;
};

Info operator + (const Info &l, const Info &r) {
	Info a;
	a.maxv = max(l.maxv, r.maxv);
    if (a.maxv == l.maxv) a.id = l.id;
    else a.id = r.id;
	return a;
}

struct Node {
	Info val;
} seg[(N + Q) * 4];

void update(int id) {
	seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}

void change(int id, int l, int r, int pos, int val) {
	if (l == r) {
		seg[id].val.maxv += val;
        seg[id].val.id = l;
	} else {
		int mid = (l + r) / 2;
		if (pos <= mid) change(id * 2, l, mid, pos, val);
		else change(id * 2 + 1, mid + 1, r, pos, val);
		update(id); 
	}
}

Info query(int id, int l, int r, int ql, int qr) {
	if (l == ql && r == qr) return seg[id].val;
	int mid = (l + r) / 2;
	if (qr <= mid) return query(id * 2, l, mid, ql, qr);
	else if (ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
	else return query(id * 2, l, mid, ql, mid) + query(id * 2 + 1, mid + 1, r, mid + 1, qr);
}

int main() {
    bool markA = true, markB = true, markC = true;
    scanf("%d%d", &n, &q);
    vector<deque<int>> a(n + q + 1);
    for (int i = 1; i <= n; i++) {
        int l;
        scanf("%d", &l);
        a[i].resize(l);
        for (int j = 0; j < l; j++) {
            scanf("%d", &a[i][j]);
            if (a[i][j] > 2) markB = false;
        }
    }
    vector<vector<int>> qry(q);
    for (int i = 0; i < q; i++) {
        int op;
        scanf("%d", &op);
        if (op == 1) {
            int x, y;
            scanf("%d%d", &x, &y);
            if (y > 2) markB = false;
            qry[i] = {op, x, y};
        } else if (op == 2) {
            markC = false;
            int x;
            scanf("%d", &x);
            qry[i] = {op, x};
        } else if (op == 3) {
            int m;
            scanf("%d", &m);
            qry[i] = {op, m};
            for (int j = 0; j < m; j++) {
                int x;
                scanf("%d", &x);
                qry[i].push_back(x);
            }
        } else { 
            markA = false;
            int x1, x2, x3;
            scanf("%d%d%d", &x1, &x2, &x3);
            qry[i] = {op, x1, x2, x3};
        }
    }

    if (markB) {
        vector<array<int, 3>> cnt(n + q + 1, array<int, 3>{});
        for (int i = 1; i <= n; i++) {
            for (auto j : a[i]) {
                ++cnt[i][j];
            }
        }
        for (auto qy : qry) {
            int op = qy[0];
            if (op == 1) {
                int x = qy[1], y = qy[2];
                ++cnt[x][y];
                a[x].push_back(y);
            } else if (op == 2) {
                int x = qy[1];
                --cnt[x][a[x].back()];
                a[x].pop_back();
            } else if (op == 3) {
                ll l1 = 0, l2 = 0;
                for (int j = 2; j < (int)qy.size(); j++) {
                    int x = qy[j];
                    l1 += cnt[x][1];
                    l2 += cnt[x][2];
                }
                if (l1 > l2) puts("1");
                else if (l2 > l1) puts("2");
                else puts("-1");
            } else {
                int x1 = qy[1], x2 = qy[2], x3 = qy[3];
                if (a[x1].size() > a[x2].size()) {
                    a[x3].swap(a[x1]);
                    cnt[x3].swap(cnt[x1]);
                    for (auto v : a[x2]) a[x3].push_back(v);
                    cnt[x3][1] += cnt[x2][1];
                    cnt[x3][2] += cnt[x2][2];
                } else {
                    a[x3].swap(a[x2]);
                    cnt[x3].swap(cnt[x2]);
                    for (auto v : a[x1]) a[x3].push_front(v);
                    cnt[x3][1] += cnt[x1][1];
                    cnt[x3][2] += cnt[x1][2];
                }
            }   
        }
        return 0;
    }

    if (n == 1 && markA) {
        const int maxl = 1e6;
        for (int i = 1; i <= n; i++) {
            for (auto j : a[i]) {
                change(1, 1, maxl, j, 1);
            }
        }
        for (auto qy : qry) {
            int op = qy[0];
            if (op == 1) {
                int x = qy[1], y = qy[2];
                change(1, 1, maxl, y, 1);
                a[x].push_back(y);
            } else if (op == 2) {
                int x = qy[1];
                change(1, 1, maxl, a[x].back(), -1);
                a[x].pop_back();
            } else if (op == 3) {
                auto ans = query(1, 1, maxl, 1, maxl);
                if (ans.maxv * 2 > (int)a[1].size())
                    printf("%d\n", ans.id);
                else
                    puts("-1");
            } else {
                assert(0);
            }
        }
        return 0;
    }

    {
        // BL
        array<int, 10010> cnt{}; 
        for (auto qy : qry) {
            int op = qy[0];
            if (op == 1) {
                int x = qy[1], y = qy[2];
                a[x].push_back(y);
            } else if (op == 2) {
                int x = qy[1];
                a[x].pop_back();
            } else if (op == 3) {
                int sumSZ = 0;
                for (int j = 2; j < (int)qy.size(); j++) {
                    int x = qy[j];
                    sumSZ += a[x].size();
                    for (auto k : a[x]) {
                        ++cnt[k];
                    }
                }
                int pos = max_element(cnt.begin(), cnt.end()) - cnt.begin();
                if (cnt[pos] * 2 > sumSZ) printf("%d\n", pos);
                else puts("-1");
                for (int j = 2; j < (int)qy.size(); j++) {
                    int x = qy[j];
                    for (auto k : a[x]) {
                        --cnt[k];
                    }
                }
            } else {
                int x1 = qy[1], x2 = qy[2], x3 = qy[3];
                a[x3].swap(a[x1]);
                for (auto v : a[x2]) a[x3].push_back(v);
            }
        }
    }
    return 0;
}

30 发罚时。。。

posted @ 2026-05-16 22:01  AKCoder  阅读(14)  评论(0)    收藏  举报