Codeforces 2149G Buratsuta 3 题解 [ 蓝 ] [ 摩尔投票 ] [ 线段树 ] [ 随机化 ] [ 主席树 ] [ 根号分治 ]

Buratsuta 3:典中典中典中典中典了属于是。

Sol.1 摩尔投票

首先维护区间出现次数大于等于 $\left \lfloor \dfrac{len}{k} \right \rfloor $ 次的数是摩尔投票板子,每次把 \(k\) 个不同的数相消即可。然后因为摩尔投票具有结合律,所以可以上线段树维护,时间复杂度 \(O(n + q\log n)\)。注意需要判断解是否合法。

注意到此题没有修改操作,所以如果直接上猫树可以做到 \(O(n\log n + q)\) 的复杂度。

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc (p << 1)
#define rc ((p << 1) | 1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 200005;
int n, q, a[N], b[N], bn;
int getrk(int x)
{
    return (lower_bound(b + 1, b + bn + 1, x) - b);
}
struct Node{
    int l, r;
    int val1, cnt1, val2, cnt2;
};
struct Segtree{
    Node tr[4 * N];
    void pushup(Node &p, Node ls, Node rs)
    {
        if(rs.cnt1 && rs.val1 == ls.val1) ls.cnt1 += rs.cnt1;
        else if(rs.cnt1 && rs.val1 == ls.val2) ls.cnt2 += rs.cnt1;
        else if(rs.cnt1)
        {
            int mn = min(rs.cnt1, min(ls.cnt1, ls.cnt2));
            rs.cnt1 -= mn;
            ls.cnt1 -= mn;
            ls.cnt2 -= mn;
            if(rs.cnt1 && ls.cnt1 == 0) ls.val1 = rs.val1, ls.cnt1 = rs.cnt1;
            else if(rs.cnt1 && ls.cnt2 == 0) ls.val2 = rs.val1, ls.cnt2 = rs.cnt1;        
        }

        if(rs.cnt2 && rs.val2 == ls.val1) ls.cnt1 += rs.cnt2;
        else if(rs.cnt2 && rs.val2 == ls.val2) ls.cnt2 += rs.cnt2;
        else if(rs.cnt2)
        {
            int mn = min(rs.cnt2, min(ls.cnt1, ls.cnt2));
            rs.cnt2 -= mn;
            ls.cnt1 -= mn;
            ls.cnt2 -= mn;
            if(rs.cnt2 && ls.cnt1 == 0) ls.val1 = rs.val2, ls.cnt1 = rs.cnt2;
            else if(rs.cnt2 && ls.cnt2 == 0) ls.val2 = rs.val2, ls.cnt2 = rs.cnt2;   
        }
        
        p.val1 = ls.val1;
        p.val2 = ls.val2;
        p.cnt1 = ls.cnt1;
        p.cnt2 = ls.cnt2;        
    }
    void build(int p, int ln, int rn)
    {
        tr[p] = {ln, rn, a[ln], 1, 0, 0};
        if(ln == rn) return;
        int mid = (ln + rn) >> 1;
        build(lc, ln, mid);
        build(rc, mid + 1, rn);
        pushup(tr[p], tr[lc], tr[rc]);
    }
    Node query(int p, int ln, int rn)
    {
        if(ln <= tr[p].l && tr[p].r <= rn) return tr[p];
        int mid = (tr[p].l + tr[p].r) >> 1;
        if(rn <= mid) return query(lc, ln, rn);
        if(ln >= mid + 1) return query(rc, ln, rn);
        Node tmp;
        pushup(tmp, query(lc, ln, rn), query(rc, ln, rn));
        return tmp;
    }
}tr1;
vector<int> vis[N];
int query_num(int l, int r, int v)
{
    return (upper_bound(vis[v].begin(), vis[v].end(), r) - lower_bound(vis[v].begin(), vis[v].end(), l));
}
void solve()
{
    cin >> n >> q;
    bn = 0;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        b[++bn] = a[i];
        vis[i].clear();
    }
    sort(b + 1, b + bn + 1);
    bn = unique(b + 1, b + bn + 1) - b - 1;
    for(int i = 1; i <= bn; i++) vis[i].push_back(0);
    for(int i = 1; i <= n; i++)
    {
        a[i] = getrk(a[i]);
        vis[a[i]].push_back(i);
    }
    for(int i = 1; i <= bn; i++) vis[i].push_back(n + 1);
    tr1.build(1, 1, n);
    for(int i = 1; i <= q; i++)
    {
        int l, r;
        cin >> l >> r;
        Node res = tr1.query(1, l, r);
        int ans1 = res.val1, ans2 = res.val2;
        if(ans1 > ans2) swap(ans1, ans2);
        int lmt = (r - l + 1) / 3, cnt3 = 0;
        if(ans1)
        {
            int cnt1 = query_num(l, r, ans1);
            if(cnt1 > lmt)
            {
                cout << b[ans1] << " ";
                cnt3++;
            }
        }
        if(ans2)
        {
            int cnt2 = query_num(l, r, ans2);
            if(cnt2 > lmt)
            {
                cout << b[ans2] << " ";
                cnt3++;
            }
        }
        if(cnt3 == 0) cout << -1 << " ";
        cout << "\n";
    }
}

int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--) solve();
    return 0;
}

Sol.2 随机化

随机在区间内选若干个点,然后判断一下出现次数最多的解是否合法。具体我没试选多少个点可以通过,但应该在 \(200\) 个左右。

Sol.3 根号分治

\(len \le \sqrt n\) 的区间暴力求解,对 \(len > \sqrt n\) 预处理出现次数在 \(\dfrac{\sqrt n}{3}\) 以上的数的出现位置,查询的时候二分即可。容易发现预处理的点大概就是 \(\sqrt n\) 量级的。所以时间复杂度为 \(O(n\sqrt n\log n)\)

posted @ 2025-09-30 16:17  KS_Fszha  阅读(49)  评论(0)    收藏  举报