• D. Cut and Stick 线段树


    D. Cut and Stick 线段树

    题目大意:

    给你一个序列 (a),保证: (a_ileq n) ,每次询问一个区间 ([l,r]) ,你可以把这个区间的数分成 (x) 个集合,设 (siz) = 这个集合的大小,要求任意一个集合内相同数的重复次数小于等于 (left lceil frac{siz}{2} ight ceil) ,问最小的 (x) 是多少?

    题解:

    因为这个题目的难点在于求区间出现频率最多的数的次数,所以这篇博客只讲如何求这个。

    线段树 (node[id]) 点存的是线段树第 (id) 这个节点表示的区间的出现频率最多的数。

    假设一个区间 ([l,r]) 出现频率最多的是 (x) ,那么将 ([l,r]) 分成 ([l,mid])([mid+1,r]) 这两个区间至少存在一个区间出现频率最多的是 (x) ,以此类推,那么最后 ([l,r]) 分解的区间,每一层至少存在一个子区间的数是 ([l,r]) 的相同。 那么最后 (node[id]) 维护出来的就是这个区间出现频率最多的数。

    假设询问区间是 ([x,y]) ,往下递归,首先可以确认的是一定存在一个子区间 ([l,r]) 的数是区间 ([x,y]) 的出现频率最多的数。所以对于每一个子区间,直接算出来这个区间节点的值 (node[id]) 在区间 ([x,y]) 内出现的次数即可,最后取一个最大值。

    #include <bits/stdc++.h>
    #define lson (id<<1)
    #define rson (id<<1|1)
    using namespace std;
    const int maxn = 3e5 + 10;
    vector<int>v[maxn];
    int a[maxn],node[maxn<<2];
    // node[id] 表示的是 id 表示的这个区间的出现频率最高的数是多少
    int cal(int x,int l,int r) {
    //    int maxs = upper_bound(v[x].begin(),v[x].end(),r) - v[x].begin();
    //    int mins = lower_bound(v[x].begin(),v[x].end(),l) - v[x].begin();
    //    printf("x = %d l = %d r = %d maxs = %d %d
    ",x,l,r,maxs,mins);
        return upper_bound(v[x].begin(), v[x].end(), r) - lower_bound(v[x].begin(), v[x].end(), l);
    }
    void build(int id,int l,int r) {
        if (l == r) {
            node[id] = a[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(lson, l, mid);
        build(rson, mid + 1, r);
        node[id] = cal(node[lson], l, mid) - cal(node[rson], mid + 1, r) > 0 ? node[lson] : node[rson];
    }
    int query(int id,int l,int r,int x,int y) {
        if (x <= l && y >= r) {
    //        printf("node[%d]=%d %d
    ",id,node[id],cal(node[id], x, y));
            return cal(node[id], x, y);
        }
        int mid = (l + r) >> 1, ans = 0;
        if (x <= mid) ans = max(ans, query(lson, l, mid, x, y));
        if (y > mid) ans = max(ans, query(rson, mid + 1, r, x, y));
    //    printf("id = %d ans = %d
    ",id,ans);
        return ans;
    }
    int main() {
        int n, q;
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]), v[a[i]].push_back(i);
        build(1, 1, n);
        while (q--) {
            int l, r;
            scanf("%d%d", &l, &r);
            int res = query(1, 1, n, l, r), len = r - l + 1;
    //        printf("res = %d
    ",res);
            if (res <= (len + 1) / 2) printf("1
    ");
            else {
                int ans = max(1, 2 * res - len);
                printf("%d
    ", ans);
            }
        }
    }
    
  • 相关阅读:
    报错处理
    MySQL8.0跟5.7分组查询表所有字段
    模拟开始时间、结束时间生成历史时间生成曲线模拟数据
    查询电脑登录过的WiFI账号密码
    Samba服务器架设
    CentOS安装GitLab
    申请域名并使用DDNS
    极路由4增强版(极企版)-刷潘多拉固件
    Git命令
    elasticsearch7.6.2 -canal1.1.4集成
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/14680636.html
Copyright © 2020-2023  润新知