D. Cut and Stick 线段树

D. Cut and Stick 线段树

题目大意:

给你一个序列 \(a\),保证: \(a_i\leq n\) ,每次询问一个区间 \([l,r]\) ,你可以把这个区间的数分成 \(x\) 个集合,设 \(siz\) = 这个集合的大小,要求任意一个集合内相同数的重复次数小于等于 \(\left \lceil \frac{siz}{2} \right \rceil\) ,问最小的 \(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\n",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\n",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\n",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\n",res);
        if (res <= (len + 1) / 2) printf("1\n");
        else {
            int ans = max(1, 2 * res - len);
            printf("%d\n", ans);
        }
    }
}
posted @ 2021-04-20 13:23  EchoZQN  阅读(172)  评论(0编辑  收藏  举报