题解 P3765 【总统选举】

斯德哥尔摩

2018-09-18 00:17:07

Solution

[P3765 总统选举](https://www.luogu.org/problemnew/show/P3765) 楼下几位好强啊,两位巨佬都没有给出代码,还有一位巨佬用了$STL$的平衡树,看得我好无奈啊。。。 首先得会这题:[P2397 yyy loves Maths VI (mode)](https://www.luogu.org/problemnew/show/P2397) 直接用对抗法/消去法找到众数即可。 那么,带上修改和多次询问呢? 也就是这题? 我是看这位巨佬的博客弄懂的:[链接](https://blog.csdn.net/Ab_Ever/article/details/72675837) 我们发现上述那个对抗法是可以进行区间合并的。 也就是能从$[l,mid],[mid+1,r]$合并成$[l,r]$。 记录当前的区间众数,区间众数在对抗后出现了多少次。 合并的时候,分两种情况: 1. 当两个区间的区间众数相等时,直接把次数相加。 2. 当两个区间的区间众数不相时,将出现次数多的作为合并后区间的区间众数,出现次数就是$\text{出现次数多的次数-出现次数少的次数}$。 这个合并让我们想起了什么? 或者说我们应该如何维护? ### 线段树! 修改直接单点修改,查询的时候因为维护了两个值:区间众数以及出现次数,直接返回一个结构体就好。 这题就做完了 吗? 并不。 因为“众数”不一定存在! 就是说未必有满足题目的答案,我们维护出的未必是正确答案,只是维护的那个数是“最具有成为‘众数’的潜质”的数罢了。 所以我们需要一个平衡树来检验我们求出的“区间众数”是否是真正的区间众数。 检验很简单,每个人开个$Treap$,判断在$[l,r]$内有几个人选他就行了。 差分一下,就可以在$Treap$上进行普通的二叉查找了。 复杂度是$O(\sum k_i\log_2n)$,可以通过。 附上长长的封装的代码: ```cpp #include<iostream> #include<algorithm> #include<cstdio> #define MAXN 500010 using namespace std; int n,m; int val[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } namespace T{//这是个指针+结构体的裸的Treap,不习惯可以自己写,因为这是纯板子。。。 struct node{ node* son[2]; int v,w,s,flag; node(){ son[0]=son[1]=NULL; w=rand(); v=0; s=flag=1; } }; node* root[MAXN]; inline void maintain(node* &u){ u->s=u->flag; if(u->son[0]!=NULL)u->s+=u->son[0]->s; if(u->son[1]!=NULL)u->s+=u->son[1]->s; } inline void turn(node* &u,int f){ node* t=u->son[f^1]; u->son[f^1]=t->son[f]; t->son[f]=u; maintain(u); maintain(t); u=t; } void insert(node* &u,int x){ if(u==NULL){ u=new node; u->v=x; return; } else if(u->v==x){ u->flag++; maintain(u); return; } int y=u->v<x?1:0; insert(u->son[y],x); if(u->son[y]->w>u->w)turn(u,y^1); else maintain(u); } void remove(node* &u,int x){ if(u==NULL)return; if(u->v==x){ if(u->flag>1)u->flag--; else{ if(u->son[0]==NULL&&u->son[1]==NULL)u=NULL; else if(u->son[0]!=NULL&&u->son[1]!=NULL){ if(u->son[0]->w>u->son[1]->w){ turn(u,1); remove(u->son[1],x); } else{ turn(u,0); remove(u->son[0],x); } } else{ if(u->son[0]==NULL)u=u->son[1]; else u=u->son[0]; } } if(u!=NULL)maintain(u); } else{ if(u->v>x)remove(u->son[0],x); else if(u->v<x)remove(u->son[1],x); if(u!=NULL)maintain(u); } } int query(node* u,int k){//这个函数是纯的二叉查找 if(u==NULL)return 0; int lsons=0; if(u->son[0]!=NULL)lsons=u->son[0]->s; if(k<u->v)return query(u->son[0],k); else if(k>=u->v)return lsons+u->flag+query(u->son[1],k); } } namespace ST{//线段树,也是纯板子,不习惯也可以自己写。 #define LSON rt<<1 #define RSON rt<<1|1 #define DATA(x) a[x].data #define NUM(x) a[x].num #define LSIDE(x) a[x].l #define RSIDE(x) a[x].r struct Segment_Tree{ int data,num,l,r; }a[MAXN<<2]; inline void pushup(int rt){ if(DATA(LSON)==DATA(RSON)){ DATA(rt)=DATA(LSON); NUM(rt)=NUM(LSON)+NUM(RSON); } else if(NUM(LSON)>NUM(RSON)){ DATA(rt)=DATA(LSON); NUM(rt)=NUM(LSON)-NUM(RSON); } else{ DATA(rt)=DATA(RSON); NUM(rt)=NUM(RSON)-NUM(LSON); } } void buildtree(int l,int r,int rt){ LSIDE(rt)=l;RSIDE(rt)=r;DATA(rt)=NUM(rt)=0; if(l==r){ DATA(rt)=val[l]; NUM(rt)=1; return; } int mid=l+r>>1; buildtree(l,mid,LSON); buildtree(mid+1,r,RSON); pushup(rt); } void update(int k,int c,int rt){ if(LSIDE(rt)==RSIDE(rt)){ DATA(rt)=c; NUM(rt)=1; return; } int mid=LSIDE(rt)+RSIDE(rt)>>1; if(k<=mid)update(k,c,LSON); else update(k,c,RSON); pushup(rt); } Segment_Tree query(int l,int r,int rt){ if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return a[rt]; int mid=LSIDE(rt)+RSIDE(rt)>>1; Segment_Tree ans,lson=(Segment_Tree){0,0,0,0},rson=(Segment_Tree){0,0,0,0}; if(l<=mid)lson=query(l,r,LSON); if(mid<r)rson=query(l,r,RSON); if(lson.data==rson.data){ ans.data=lson.data; ans.num=lson.num+rson.num; } else if(lson.num>rson.num){ ans.data=lson.data; ans.num=lson.num-rson.num; } else{ ans.data=rson.data; ans.num=rson.num-lson.num; } return ans; } } void work(){ int l,r,s,k,x,ans; while(m--){ l=read();r=read();s=read();k=read(); ans=ST::query(l,r,1).data; if((T::query(T::root[ans],r)-T::query(T::root[ans],l-1))<=(r-l+1)/2)ans=s; //检验答案 printf("%d\n",ans);//求出答案 while(k--){//修改 x=read(); ST::update(x,ans,1); T::remove(T::root[val[x]],x); val[x]=ans; T::insert(T::root[val[x]],x); } } ans=ST::query(1,n,1).data; if((T::query(T::root[ans],r)-T::query(T::root[ans],l-1))<=n/2)ans=-1; //检验答案 printf("%d\n",ans);//最后还有一遍。。。 } void init(){//读入+预处理 srand(2002);//随机种子随便 n=read();m=read(); for(int i=1;i<=n;i++){ val[i]=read(); T::root[i]=NULL; } ST::buildtree(1,n,1); for(int i=1;i<=n;i++)T::insert(T::root[val[i]],i); } int main(){//主函数So easy! init(); work(); return 0; } ```