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");
}
}
}