题解 P3765 【总统选举】
原文链接
很有意思的一道题。
Part.1
一开始拿到这道题一看,题目要求的是区间众数的出现次数,这个显然没法做,据我所知只有静态分块或者回滚莫队可以做不带修版本的,但是他要我们求的比这个要弱许多,我们可以发现一个性质,显然如果一个数出现次数超过一半,我们让它与剩下的数两两抵消,剩下的数必然只有它自己。更进一步,如果区间中一旦有一个满足条件的数,我们任意选不同的两个数抵消,剩下的必然是那个数。
我们用数据结构实现这个过程。
考虑一棵线段树,她维护区间中两两抵消还剩哪个数以及还剩多少。她的左右儿子信息合并时,我们看两个儿子剩下的数是否一样,如果一样就相加,如果不一样就取最大,然后互相抵消。
Part.2
然后,你以为做完了?
如果真做完了那么它最多是绿题。
我们刚才假设了区间绝对满足条件,那么如果区间不满足条件呢?暴力找
一个显然的做法是对每一个总统建一个\(vector\),每次找出答案后再\(vector\)中二分询问区间的左右端点,然后看中间数的个数是否大于\(len/2\)就可以了。
然而题目中有修改。暴力修改
于是实际上,我们要支持一个数据结构,插入一个数,删除一个数,查询某个数的排名(相当与二分)
这是什么?平衡树板子。
然后,然后就真的完了。
总结一下,我们每次在线段树上找出一个数,然后再在平衡树上找出区间中该数的个数检查是否合法就可以了。
Part.3
安利一下\(LeafyTree\),即\(WBLT\),她的形态与线段树差不多,常数小跑的飞快,一般比\(Splay\)或\(FHQTreap\)快两到三倍。
最后一个细节,在找排名的时候,一路走到叶子后一定要看一下这个数是不是比叶子大,如果小要\(return\;0\),因为这个数可能比平衡树中的所有数都小。
Code
struct Node
{
int siz,val;
Node *lc,*rc;
Node(int siz,int val,Node *lc,Node *rc) : siz(siz),val(val),lc(lc),rc(rc) {}
Node() {}
}*null,*stk[maxn*3],utk[maxn*3];
int utot,a[maxn],n;
struct Leafy_Tree
{
Node *rt[maxn];
#define newnode(a,b,c,d) (&(*stk[utot++]=Node(a,b,c,d)))
#define merge(a,b) newnode(a->siz+b->siz,b->val,a,b)
inline void init()
{
for(int i=1;i<=n;++i)
rt[i]=newnode(1,0x3f3f3f3f,null,null);
}
inline void rotate(Node *u)
{
if(u->lc->siz > u->rc->siz*ratio)
u->rc=merge(u->lc->rc,u->rc),stk[--utot]=u->lc,u->lc=u->lc->lc;
else if(u->rc->siz > u->lc->siz*ratio)
u->lc=merge(u->lc,u->rc->lc),stk[--utot]=u->rc,u->rc=u->rc->rc;
}
inline void pushup(Node *u)
{
if(!u->lc->siz) return;
u->siz=u->lc->siz+u->rc->siz;
u->val=u->rc->val;
}
inline void insert(Node *u,int val)
{
if(u->siz==1)
{
u->lc=newnode(1,min(u->val,val),null,null);
u->rc=newnode(1,max(u->val,val),null,null);
}
else insert(val > u->lc->val?u->rc:u->lc,val);
pushup(u);rotate(u);
}
inline void erase(Node *u,int val)
{
if(u->lc->siz==1&&u->lc->val==val)
stk[--utot]=u->lc,stk[--utot]=u->rc,*u=*u->rc;
else if(u->rc->siz==1&&u->rc->val==val)
stk[--utot]=u->lc,stk[--utot]=u->rc,*u=*u->lc;
else erase(val > u->lc->val?u->rc:u->lc,val);
pushup(u);rotate(u);
}
inline int rnk(Node *u,int val)
{
if(u->siz==1) return val>=u->val;
return val > u->lc->val ? rnk(u->rc,val)+u->lc->siz : rnk(u->lc,val);
}
}leafy;
struct Segment_Tree
{
int mx[maxn],cnt[maxn];
inline void pushup(int u)
{
if(mx[u<<1]==mx[u<<1|1]) mx[u]=mx[u<<1],cnt[u]=cnt[u<<1]+cnt[u<<1|1];
else if(cnt[u<<1]>cnt[u<<1|1]) mx[u]=mx[u<<1],cnt[u]=cnt[u<<1]-cnt[u<<1|1];
else mx[u]=mx[u<<1|1],cnt[u]=cnt[u<<1|1]-cnt[u<<1];
}
inline void build(int u,int l,int r)
{
if(l==r) {mx[u]=a[l],cnt[u]=1;return;}
int mid=(l+r)>>1;
build(u<<1,l,mid);build(u<<1|1,mid+1,r);
pushup(u);
}
inline void modify(int u,int l,int r,int x,int val)
{
if(l==r) {mx[u]=val;return;}
int mid=(l+r)>>1;
if(x<=mid) modify(u<<1,l,mid,x,val);
else modify(u<<1|1,mid+1,r,x,val);
pushup(u);
}
inline pii query(int u,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return make_pair(mx[u],cnt[u]);
int mid=(l+r)>>1;
if(y<=mid) return query(u<<1,l,mid,x,y);
else if(x>mid) return query(u<<1|1,mid+1,r,x,y);
else
{
pii a=query(u<<1,l,mid,x,y),b=query(u<<1|1,mid+1,r,x,y),c;
if(a.first==b.first) c.first=a.first,c.second=a.second+b.second;
else if(a.second>b.second) c.first=a.first,c.second=a.second-b.second;
else c.first=b.first,c.second=b.second-a.second;
return c;
}
}
}seg;
inline void modify(int x,int val)
{
leafy.erase(leafy.rt[a[x]],x);
leafy.insert(leafy.rt[val],x);
seg.modify(1,1,n,x,val);
a[x]=val;
}
inline int query(int x,int y)
{
int ans=seg.query(1,1,n,x,y).first;
if(((y-x+1)>>1)<leafy.rnk(leafy.rt[ans],y)-leafy.rnk(leafy.rt[ans],x-1)) return ans;
else return -1;
}
template<typename T>
inline void read(T &x)
{
char c;int f=1;
while(!isdigit(c=getchar())) (c=='-')&&(f=-1);
x=c^48;
while(isdigit(c=getchar())) x=x*10+(c^48);
x*=f;
}
int main()
{
int l,r,s,k,m,x;
for(int i=0;i<maxn*3;++i)
stk[i]=&utk[i];
null=new Node(0,0,0,0);
read(n);read(m);
for(int i=1;i<=n;++i)
read(a[i]);
leafy.init();
seg.build(1,1,n);
for(int i=1;i<=n;++i)
leafy.insert(leafy.rt[a[i]],i);
while(m--)
{
read(l);read(r);read(s);read(k);
x=query(l,r);
if(x!=-1) s=x;
printf("%d\n",s);
while(k--)
read(x),modify(x,s);
}
printf("%d\n",query(1,n));
return 0;
}