题解 CF1354D 【Multiset】
考试拿到题,一看,这不是权值线段树吗?
思路
使用线段树每个节点维护该区间内元素出现次数。
根据题目,对于加入、删除元素,我们可以单点修改(\(+1\)、\(-1\)),对于输出,我们可 随便 遍历找一个出现次数为 \(1\) 的元素即可。
代码
具体解释见注释
#include<bits/stdc++.h>
#define re read()
using namespace std;
const int MAXN=1e6+10;
int n,q,A[MAXN];
struct SEGTREE{
int sum;//sum 最大为10^6 无需long long(我第一次手残开了,然后爆了空间)
}tr[MAXN<<2];
int read(){//快读
#define gt getchar()
#define isdi(a) (a>='0'&&a<='9')
int x=0,sgn=1;char ch=gt;
for(;!isdi(ch);ch=gt)if(ch=='-')sgn=-1;
for(;isdi(ch);ch=gt)x=(x<<1)+(x<<3)+(ch^48);
return x*sgn;
}
void build(int k,int l,int r){ //建树
if(l==r){
tr[k].sum=A[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid); build(k<<1|1,mid+1,r);
tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
return;
}
void add(int k,int l,int r,int num){//加入元素
if(l==r){
if(l==num)tr[k].sum++;
return;
}
int mid=(l+r)>>1;
if(num>mid)add(k<<1|1,mid+1,r,num);
else add(k<<1,l,mid,num);
tr[k].sum++;
return;
}
void del(int k,int l,int r,int num){
//删除该区间内第num大的元素
if(l==r){
if(tr[k].sum)tr[k].sum--;
return;
}
if(num>tr[k].sum)return;
int mid=(l+r)>>1;
if(num>tr[k<<1].sum)del(k<<1|1,mid+1,r,num-tr[k<<1].sum);
//如果num大于左子树元素出现的总次数,则去右子树删第(num-左子树元素出现总次数)大的数
else if(num<=tr[k<<1].sum)del(k<<1,l,mid,num);
//否则去左子树删除第num大的数
else return;
tr[k].sum--;
//该区间内的次数减一
return;
}
int find(int k,int l,int r){//递归查找
if(l==r){
if(tr[k].sum)return l;
return 0;
}
int mid=(l+r)>>1;
if(tr[k<<1].sum)return find(k<<1,l,mid);
else return find(k<<1|1,mid+1,r);
return 0;
//根据题目,随便找一个就行了,找不到就输出0
}
int main (){
n=re;q=re;
for(int i=1;i<=n;i++){
int temp=re;
A[temp]++;
}
build(1,1,n);
for(int i=1;i<=q;i++){
int k=re;
if(k<0)del(1,1,n,-k);
else add(1,1,n,k);
}
printf("%d",find(1,1,n));
return 0;
}
本博客作者:Werner_Yin(https://www.cnblogs.com/werner-yin/) ,转载时请注明出处,谢谢支持!