[线段树] Chef and Array

本文介绍了一个基于线段树和哈希映射实现的算法,用于处理序列中的支配者查询及更新操作。通过高效的单点更新和子序列查询,解决了特定类型的序列分析问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CHEFLKJ: Chef and Array
题目描述
大厨喜欢序列和有关序列的一切。他称一个序列 V 是支配的,当且仅当存在一个数字 x(即“支配者”)其在序列中的出现次数严格超过序列长度的一半(即 ⌊|V |/2⌋)。
他的朋友 Dmytro 想让大厨开心,因此他给大厨呈现了这道有趣的题目:
给定序列 A,你需要处理 Q 个询问。询问分为两类:
• 1 x y:令序列的第 x 个元素的值为 y,即令 Ax = y;
• 2 l r:询问子序列 A[l..r] 是否是支配的,如果是则输出“Yes”,否则输出“No”。
输入格式
输入的第一行包含两个整数 N 和 Q,分别代表 A 中的元素数和需要处理的询问数。
第二行包含 N 个由空格分隔的整数,代表序列 A。
接下来 Q 行,每行描述一个询问。
输出格式
对于每个第 2 类的询问,输出一行,包含询问的回答。
数据范围
• 1 ≤ N, Q ≤ 10^5
• 1 ≤ Ai,y ≤ 10^9
• 1 ≤ x ≤ N
• 1 ≤ l ≤ r ≤ N

分析:
先用线段树+map树套树暴力存所有数,然后更新就是经典的单点更新,再处理查询。
假设x是[l,r]的支配者,根据鸽巢原理,x也必须是左儿子的支配者,或者是右儿子的支配者(否则x的个数必然不超过长度的一半,矛盾),然后发现这只是个必要条件,而非充分条件,即左右儿子的支配者不一定是节点的支配者,所以查询的时候往左右找到所有支配者,暴力判断是不是支配者。
似乎并不用启发式合并。

#include<bits/stdc++.h>
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int N = 1e5+5;
unordered_map<int,int>tree[N<<2];
int domi[N<<2];
int a[N];
void push_up(int rt, int len){
    int lrt = rt<<1, rrt = rt<<1|1;
    if(tree[rt][domi[lrt]] > len) domi[rt] = domi[lrt];
    else if(tree[rt][domi[rrt]] > len) domi[rt] = domi[rrt];
    else domi[rt] = -1;
}
void build(int rt, int l, int r){
    if(l == r){
        tree[rt][a[l]]++;
        domi[rt] = a[l];
        return;
    }
    int mid = (l+r)>>1;
    build(lson);
    build(rson);
    tree[rt] = tree[rt<<1];
    for(auto x : tree[rt<<1|1]) tree[rt][x.first] += x.second;
    push_up(rt, (r-l+1)/2);
}
void update(int rt, int l, int r, int pos, int pre, int val){
    if(l == r){
        tree[rt].clear();
        a[pos] = val;
        tree[rt][val]++;
        domi[rt] = val;
        return;
    }
    int mid = (l+r) >> 1;
    if(pos <= mid) update(lson, pos, pre, val);
    else update(rson, pos, pre, val);
    tree[rt][pre] -= 1;
    tree[rt][val] += 1;
    push_up(rt, (r-l+1)/2);
}
vector<int>qry;
void query(int rt, int l, int r, int ql, int qr){
    if(ql <= l && qr >= r){ qry.push_back(rt); return; }
    int mid = (l+r)>>1;
    if(ql <= mid) query(lson, ql, qr);
    if(qr > mid) query(rson, ql, qr);
}
int main(){
    int n, q;
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; ++i) scanf("%d", a+i);
    build(1, 1, n);
    while(q--){
        int t, x, y;
        scanf("%d%d%d", &t, &x, &y);
        if(t == 1) update(1, 1, n, x, a[x], y);
        else{
            int flag = 0;
            qry.clear();
            query(1, 1, n, x, y);
            for(int i = 0; i < qry.size() && !flag; ++i){
                int cnt = 0;
                for(int j = 0; j < qry.size() && !flag; ++j){
                    cnt += tree[qry[j]][domi[qry[i]]];
                }
                if(cnt > (y-x+1)/2){
                    puts("Yes");
                    flag = 1;
                    break;
                }
            }
            if(!flag) puts("No");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值