P8496 [NOI2022] 众数

分析

假如没有操作 33,怎么维护这些序列呢?由于修改只在末尾进行,于是我们可以仿照建图时的头插法将一个序列的数字插入邻接表中,这样 headxhead_x 就是序列 xx 的末尾了。

但是,操作 44 的合并时,我们需要让 x2x_2 的首个数字的 nxtnxt 指针指向 x1x1 的末尾数字,因此可以再记录一个 tailxtail_x 表示邻接表的末尾(也就是序列 xx 的首个数字):

void add(int x,int y){
	val[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
	if(!nxt[tot])
		tail[x]=tot;
}
void del(int x){
	head[x]=nxt[head[x]];
	if(!head[x])
		tail[x]=0;
}

对于操作 44 你可能会这么写:

//x,y,z分别表示x1,x2,x3 
x=read();y=read();z=read();
head[z]=head[y];
tail[z]=tail[x];
nxt[tail[y]]=head[x];

然而交上去只有 8585 分,WA on #13 #19 #20\text{WA on \#13 \#19 \#20},原来题目中说的是 lil_i 为非负整数,后面的删除操作也可能使 lil_i 变为 00,这个 00 则可能使邻接表中多出一个数字 00,切断了邻接表,因此必须加一些特判:

//x,y,z分别表示x1,x2,x3 
x=read();y=read();z=read();
head[z]=(head[y]?head[y]:head[x]);
tail[z]=(tail[x]?tail[x]:tail[y]);
if(head[x]&&head[y])
	nxt[tail[y]]=head[x];

这样对序列的维护就解决了,接下来就是求众数,因为题目中要求众数的出现次数必须大于数的个数的一半,所以就可以用摩尔投票法解决,对于每个序列开一个动态开点权值线段树,维护一个区间的众数和它在摩尔投票中剩余的票数,向上传递信息时,众数为票数较大的那一个,剩余的票数就是多的减去少的。合并 mm 个序列时,只需要将 mm 颗线段树的众数用摩尔投票法合并即可,别忘了最后统计众数的出现的次数是否超过一半。

这样,对于操作 11,直接单点加,对于操作 22,我们用邻接表得到末尾数字并单点减,对于操作 44 就可以线段树合并,这题就解决了。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=1e6+10;
int n,q,l[N],xx[N];
int head[N],nxt[N],val[N],tail[N],tot;
void add(int x,int y){
	val[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
	if(!nxt[tot])
		tail[x]=tot;
}
void del(int x){
	head[x]=nxt[head[x]];
	if(!head[x])
		tail[x]=0;
}
void cmp(pair<int,int> &x,pair<int,int> y,pair<int,int> z){
	if(y.first==z.first){
		x.first=y.first;
		x.second=y.second+z.second;
	}
    else if(y.second>z.second){
    	x.first=y.first;
		x.second=y.second-z.second;
	}
    else{
    	x.first=z.first;
		x.second=z.second-y.second;
	}
}
#define pl a[p].tl
#define pr a[p].tr
struct Segment_Tree{
    int tot,root[N],cnt,nc[N<<3];
    struct Tree{
        int tl,tr;
        int vsum;
        pair<int,int> zs;
    }a[N<<3];
    Segment_Tree(){
        tot=0;
    }
    int get_new(){
        return cnt?nc[cnt--]:++tot;
    }
    void del(int p){
        nc[++cnt]=p;
        a[p].tl=a[p].tr=a[p].zs.first=a[p].zs.second=a[p].vsum=0;
    }
    void pushup(int p){
    	cmp(a[p].zs,a[pl].zs,a[pr].zs);
        a[p].vsum=a[pl].vsum+a[pr].vsum;
    }
    void add(int p,int x,int v,int L,int R){
        if(L==R){
        	a[p].zs.first=L;
            a[p].zs.second+=v;
            a[p].vsum+=v;
            return;
        }
        int mid=(L+R)>>1;
        if(x<=mid){
            if(!pl)
                pl=get_new();
            add(pl,x,v,L,mid);
        }
        else{
            if(!pr)
                pr=get_new();
            add(pr,x,v,mid+1,R);
        }
        pushup(p);
    }
    int asksum(int p,int l,int r,int L,int R){
        if(l<=L&&R<=r)
            return a[p].vsum;
        int mid=(L+R)>>1;
        int ans=0;
        if(!pl)
            pl=get_new();
        if(!pr)
            pr=get_new();
        if(l<=mid)
            ans+=asksum(pl,l,r,L,mid);
        if(r>mid)
            ans+=asksum(pr,l,r,mid+1,R);
        return ans;
    }
    int merge(int x,int y,int l,int r){
        if(!x||!y)
            return x+y;
        if(l==r){
        	a[x].zs.first=l;
        	a[x].zs.second+=a[y].zs.second;
        	a[x].vsum+=a[y].vsum;
        	return x;
		}
		int mid=(l+r)>>1;
        a[x].tl=merge(a[x].tl,a[y].tl,l,mid);
        a[x].tr=merge(a[x].tr,a[y].tr,mid+1,r);
        del(y);
        pushup(x);
        return x;
    }
}tree;
int main(){
	n=read();q=read();
	for(int i=1;i<=n;i++){
		tree.root[i]=tree.get_new();
	}
	for(int i=1;i<=n;i++){
		l[i]=read();
		for(int j=1;j<=l[i];j++){
			int x=read();
			add(i,x);
			tree.add(tree.root[i],x,1,1,n+q);
		}
	}
	for(int i=1;i<=q;i++){
		ll op,m,x,y,z;
		op=read();
		if(op==1){
			x=read();y=read();
			add(x,y);
			tree.add(tree.root[x],y,1,1,n+q);
		}
		else if(op==2){
			x=read();
			tree.add(tree.root[x],val[head[x]],-1,1,n+q);
			del(x);
		}
		else if(op==3){
			m=read();
			pair<int,int>u;
			u.first=u.second=0;
			for(int j=1;j<=m;j++){
				xx[j]=read();
				cmp(u,u,tree.a[tree.root[xx[j]]].zs);
			}
			if(!u.first){
				puts("-1");
				continue;
			}
			x=0;y=0;
			for(int j=1;j<=m;j++){
				x+=tree.asksum(tree.root[xx[j]],1,n+q,1,n+q);
				y+=tree.asksum(tree.root[xx[j]],u.first,u.first,1,n+q);
			}
			if(y*2>x){
				write(u.first);
				puts("");
			}
			else{
				puts("-1");
			}
		}
		else{
			//x,y,z分别表示x1,x2,x3 
			x=read();y=read();z=read();
			tree.root[z]=tree.get_new();
			tree.merge(tree.root[z],tree.root[x],1,n+q);
			tree.merge(tree.root[z],tree.root[y],1,n+q);
			head[z]=(head[y]?head[y]:head[x]);
			tail[z]=(tail[x]?tail[x]:tail[y]);
			if(head[x]&&head[y])
				nxt[tail[y]]=head[x];
			
		}
	}
	return 0;
}
posted @ 2022-08-29 22:19  luckydrawbox  阅读(9)  评论(0)    收藏  举报  来源