ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[NOI2022] 众数 题解

2022-08-30 17:33:34  阅读:238  来源: 互联网

标签:int 题解 线段 链表 tl NOI2022 众数 id


权值线段树

权值线段树即一种线段树,以序列的数值为下标
权值线段树维护一列数中数的个数

也就是说,我们的权值线段树就是用线段树维护了一堆桶。

这就是权值线段树的概念。

权值线段树维护的是桶,按值域开空间,维护的是个数

[NOI2022] 众数

这个题:我们可以不是很显然地知道:众数为中位数。。。。。

浅浅地证明一下啊:

若众数在最左边

众数为序列中出现次数严格大于一半的数字

∴\(a[1\to ed]=same,ed-1+1(len_{众数})≥\frac{1}{2}len_{all}\)

∴\(1≤mid<ed\)

∴\(a[mid]=a[1]\)

即中位数=众数

若众数在最右边,同理可得中位数=众数

若在中间:

若众数为\(a[w]\)

\(len>mid\)

∴\(a[w\to w+len]=same\)

又∵\(w+len≤n\)

∴\(w<mid\)

又因为\(w+len>mid\)

∴\(a[mid]=a[w]\)

综上所述,众数为中位数

证毕。

so,我们可以开一个权值线段树来统计序列中数的个数

用链表来存序列。。。。。。。。。。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4e6+2; 
int n;//初始 有多少链表 
int q;//询问个数 
int m;//询问3链表的个数 
int que[N];// 询问3的第i个链表的id 
int lst[N];//询问3的第i个链表的rt值 
int lim;//总共有多少链表 
int hd[N];//i链表的头头 
int tl[N];//i链表的尾巴 
int pre[N];//i的前面一个 
int val[N];//第i个数的值 
int tot;//加入数的id 
ll sz[N];//i链表的size大小 
int rt[N];//i链表在线段树上的rt 
int lc[N],rc[N];//线段树i的左右儿子 
int ttt;//线段树节点(动态开点) 
ll sum[N];//i的个数(线段树)
int x,op,id,y;
void addlink(int id,int x){//在链表后加上x 
    if(!sz[id]) hd[id]=x;//x就是id链表的第一个元素(ta就是头头) 
    sz[id]++;//链表大小更新 
    pre[x]=tl[id];//x的前一个就是id链表之前的尾巴 
    tl[id]=x;//新尾巴 
} 
void dellink(int id){//删去链表末尾 
    sz[id]--;// 链表大小更新
    if(!sz[id]) hd[id]=0;//id链表没了。。 
    tl[id]=pre[tl[id]];//尾巴就是尾巴的前一个 
} 
void link(int x,int y,int id){//链表合并 
    if(sz[x])hd[id]=hd[x];//id的头头就是x的头头 
    else hd[id]=hd[y];//但是如果xGG了话就是y的头头了 
    if(sz[y])tl[id]=tl[y];//id的尾巴就是y的尾巴  
    else tl[id]=tl[x];//但是如果yGG了话就是x的尾巴了 
    sz[id]=sz[x]+sz[y];//id的大小就是x和y加起来 
    if(hd[y]) pre[hd[y]]=tl[x];//xy首位拼接 
}
void pushup(int p){
    sum[p]=sum[lc[p]]+sum[rc[p]];//sum数组更新 
}
void update(int &p,int l,int r,int w,ll v){
    if(!p){
        p=++ttt; //动态开点 
    } //懂? 
    if(l==r){//找到啦 
        sum[p]+=v;
        return ;
    }
    int mid=(l+r)>>1;
    if(w<=mid) update(lc[p],l,mid,w,v);//在左边 
    else update(rc[p],mid+1,r,w,v);//在右边 
    pushup(p);
} 
int merge(int x,int y,int l,int r){
    if(!x||!y) return (x+y);//有一个已经GG了 
    if(l==r){// 
        sum[x]+=sum[y];
        return x;
    }
    int mid=(l+r)>>1;
    lc[x]=merge(lc[x],lc[y],l,mid);
    rc[x]=merge(rc[x],rc[y],mid+1,r);
    pushup(x);//合并合并 afasdfasd 
    return x;
}
ll getsum(int p,int l,int r,int L){//L出现的次数 
    if(!p) return 0;
    if(l==r) return sum[p];//you got it! 
    int mid=(l+r)>>1;
    if(L<=mid) return getsum(lc[p],l,mid,L);
    return getsum(rc[p],mid+1,r,L);
} 
int query(int l,int r,int k){//二分 中位数 
    if(l==r) return l;
    ll tmp=0;
    for(int i=1;i<=m;i++){
        tmp+=sum[lc[lst[i]]];
    }
    int mid=(l+r)>>1;
    if(tmp>=k){//在← 
        for(int i=1;i<=m;i++){
            lst[i]=lc[lst[i]];
        } 
        return query(l,mid,k);
    }else{
        for(int i=1;i<=m;i++){
            lst[i]=rc[lst[i]];
        } 
        return query(mid+1,r,k-tmp);
    }
}
int main(){
    scanf("%d%d",&n,&q);
    lim=n+q;
    for(int i=1;i<=n;i++){
        scanf("%d",&m);
        for(int j=1;j<=m;j++){
            scanf("%d",&x);
            val[++tot]=x;
            addlink(i,tot);
            update(rt[i],1,lim,x,1); 
        }
    }
    while(q--){
        scanf("%d",&op);
        if(op==1){
            scanf("%d%d",&id,&x);
            val[++tot]=x;
            addlink(id,tot);
            //链表末尾加上x(val[tot]) 
            update(rt[id],1,lim,x,1);
            //cnt[x]+1 
        } else if(op==2){
            scanf("%d",&id);
            update(rt[id],1,lim,val[tl[id]],-1);
            //cnt[val[链表id末尾tl[id]]]-1 
            dellink(id);
            //删去链表末尾 
        } else if(op==3){
            scanf("%d",&m);
            ll all=0;//合并后数组长度
            for(int i=1;i<=m;i++){
                scanf("%d",&que[i]);//第i个数组
                lst[i]=rt[que[i]];//第i个数组的头头
                all+=sz[que[i]];//总长 
            } 
            int xx=query(1,lim,(all+1)>>1);
            ll tmp=0;
            //tmp:中位数xx出现的个数 
            for(int i=1;i<=m;i++){
                tmp+=getsum(rt[que[i]],1,lim,xx);
                //xx在第i个数组中出现的次数 
            }
            if((tmp<<1)>all) printf("%d\n",xx);//ok啊 
            else printf("-1\n"); //不ok 
        } else{
            scanf("%d%d%d",&x,&y,&id);
            link(x,y,id);//合并两链表 
            rt[id]=merge(rt[x],rt[y],1,lim); //合并线段树 
        }
    }
    return 0;
}

标签:int,题解,线段,链表,tl,NOI2022,众数,id
来源: https://www.cnblogs.com/Aurora1217/p/16640142.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有