Luogu P5062 [Ynoi Easy Round 2014] 在太阳西斜的这个世界里 题解

P5062 [Ynoi Easy Round 2014] 在太阳西斜的这个世界里

先学会红黑树。期望一眼诈骗换成计数。根据对红黑树的了解或者样例中的图可以知道旋转会发生当且仅当双红修正时子红节点的叔节点为黑点。此时如果两个红节点共线转一次,否则转两次。另外还有黑点变红的情况,很难维护。

考虑在红黑树上维护这个东西。首先维护的肯定是子树内信息,我们把旋转挂到爷爷节点,考虑对每个点维护所有能插入这棵子树内的值造成的旋转次数的和。

我们把插入的红节点视为把儿子变红,那统计旋转的方案数就是对每个子节点为一红一黑的黑点统计其红子节点左右儿子变红的方案数,称为答案。因此,我们对黑点还需要维护它变红的方案数,对红点还需要维护它的左右儿子分别变红的方案数,因为统计贡献时由于不共线转两次一个是 \(1\) 贡献一个是 \(2\) 贡献。

对于红点,pushup 时累加子树答案,然后直接更新左右儿子变红的方案数。特别的,如果是空节点,我们需要记录这个节点的前驱(或后继),作差求出插入的节点的方案数,因为插入相当于变红。前驱可以在插入时在红黑树上走路时顺便维护。

对于黑点,pushup 时累加子树答案。黑点变红当且仅当两个儿子都是红点且孙子变红,可以直接累加子节点左右儿子变红的方案数。如果子节点是一黑一红,那么我们根据红点的左右儿子变红的方案数看是否共线确定累加系数统计到答案。如果子节点是不会产生贡献,不用管它。

这样根节点就是 \([1,10^8]\) 内插入每个数的旋转次数和。为了求一段前缀 \([1,x]\),我们考虑插入一个节点 \(x\),不进行双红修正且不统计这个节点,从这个节点无论黑红只统计左子树的贡献 pushup 到根,这就求出来 \([1,x)\) 的方案数。\(x\) 的方案数我们直接暴力模拟就行了。

记录修改到的节点,查询结束后暴力回滚修改回去即可。时间复杂度 \(O(n\log n)\)

实现细节主要是只统计左子树的贡献 pushup 到根这一块。虽然不考虑右子树的贡献,但是黑点 pushup 时依旧需要考虑右子节点的颜色。因为只是不算贡献,那个节点还存在。

只统计左子树的贡献 pushup 还要注意虽然插入一个 \(x\),但是这个节点实际上不应该被统计答案,需要被记为黑空节点。但这里需要更新前驱后继,不然叶子节点可能会出锅。

至于重复元素,要相信自己的红黑树会处理好的,不需要特判。

#include <bits/stdc++.h>
using namespace std;
int n,op,x,ch[200001][2],fa[200001],val[200001],col[200001],ans[200001],chgl[200001],chgr[200001],pr[200001],nxt[200001],rt=0,cnt=0,ou=0;
int tch[200001][2],tfa[200001],tval[200001],tcol[200001],tans[200001],tchgl[200001],tchgr[200001],tpr[200001],tnxt[200001],st[200001],top=0,trt=0,w=0;
bool vis[200001],deb[200001];
bool wh(int x)
{
	return ch[fa[x]][1]==x;
}

void copy(int x)
{
	if(vis[x]||x==0)return;
	st[++top]=x,vis[x]=1,tch[x][0]=ch[x][0],tch[x][1]=ch[x][1],tfa[x]=fa[x],tval[x]=val[x],tcol[x]=col[x],tans[x]=ans[x],tchgl[x]=chgl[x],tchgr[x]=chgr[x],tpr[x]=pr[x],tnxt[x]=nxt[x];
}

void pushup(int x)
{
	if(x==0)return;
	if(col[x]==0)
	   {
	   	ans[x]=ans[ch[x][0]]+ans[ch[x][1]],chgr[x]=0;
	   	if(col[ch[x][0]]&&col[ch[x][1]])chgl[x]=chgl[ch[x][0]]+chgl[ch[x][1]]+chgr[ch[x][0]]+chgr[ch[x][1]];
	   	else chgl[x]=0;
	    if(col[ch[x][0]]&&!col[ch[x][1]])ans[x]+=(chgl[ch[x][0]]+chgr[ch[x][0]]*2);
	    if(!col[ch[x][0]]&&col[ch[x][1]])ans[x]+=(chgl[ch[x][1]]*2+chgr[ch[x][1]]);
	   }
	else
	   {
	   	ans[x]=ans[ch[x][0]]+ans[ch[x][1]];
	   	if(ch[x][0])chgl[x]=chgl[ch[x][0]];
	   	else chgl[x]=val[x]-pr[x];
	   	if(ch[x][1])chgr[x]=chgl[ch[x][1]];
	   	else chgr[x]=nxt[x]-val[x];
	   }
}

void pushupl(int x,int k=1)
{
	if(x==0)return;
	if(col[x]==0)
	   {
	   	ans[x]=ans[ch[x][0]],chgl[x]=0;
	   	if(col[ch[x][0]]&&col[ch[x][1]]&&ch[x][1]!=cnt)chgl[x]=chgl[ch[x][0]]+chgr[ch[x][0]];
	    if(col[ch[x][0]]&&(!col[ch[x][1]]||ch[x][1]==cnt))ans[x]+=(chgl[ch[x][0]]+chgr[ch[x][0]]*2);
	    chgr[x]=0;
	   }
	else
	   {
	   	ans[x]=ans[ch[x][0]];
	   	if(k==0)w=val[x]-pr[x];
	   	if(ch[x][0])chgl[x]=chgl[ch[x][0]];
	   	else chgl[x]=(val[x]-pr[x])*k;
	   	chgr[x]=0;
	   }
}

int create(int k,int fr,int l,int r,int vir)
{
	if(vir)copy(cnt+1);
	val[++cnt]=k,fa[cnt]=fr,col[cnt]=1,ans[cnt]=chgl[cnt]=chgr[cnt]=0,pr[cnt]=l,nxt[cnt]=r;
	pushup(cnt);
	return cnt;
}

void rotate(int x,int vir)
{
	int y=fa[x],z=fa[y],id=wh(x);
	if(vir)copy(x),copy(y),copy(z),copy(ch[x][id^1]),ou+=w;
	if(z)ch[z][wh(y)]=x;
	else rt=x;
	fa[x]=z;
	ch[y][id]=ch[x][id^1],fa[ch[x][id^1]]=y;
	fa[y]=x,ch[x][id^1]=y;
	pushup(y),pushup(x);
}

int upall(int x,int k,int vir)
{
	while(fa[x])
	   {
	   	if(vir)copy(fa[x]);
	   	if(wh(x)==1||k==0)
		   {
		   if(vir&&x==cnt)pushupl(fa[x]);
		   else pushup(fa[x]);
	       }
	   	else
	   	   {
		   if(vir&&x==cnt)ans[fa[x]]=0,chgl[fa[x]]=chgr[fa[x]]=0;
		   else pushupl(fa[x]);
	       }
	   	x=fa[x];
	   }
	return ans[rt];
}

void fix(int x,int vir)
{
	if(col[fa[x]]&&col[ch[fa[fa[x]]][wh(fa[x])^1]])
	   {
	   if(vir)copy(fa[x]),copy(ch[fa[fa[x]]][wh(fa[x])^1]),copy(fa[fa[x]]);
	   col[fa[x]]=0,col[ch[fa[fa[x]]][wh(fa[x])^1]]=0,col[fa[fa[x]]]=1;
	   pushup(fa[x]),pushup(ch[fa[fa[x]]][wh(fa[x])^1]),pushup(fa[fa[x]]);
	   if(col[fa[fa[fa[x]]]])fix(fa[fa[x]],vir);
       }
    else
       {
       	if(wh(x)!=wh(fa[x]))
       	   {
       	   int y=fa[x];
		   rotate(x,vir);
		   x=y;
	       }
       	int y=fa[x],z=fa[fa[x]];
       	if(vir)copy(y),copy(z);
       	col[y]=0,col[z]=1,rotate(y,vir);
	   }
}

void insert(int &x,int fr,int k,int l,int r,int vir)
{
	if(x==0)
       {
       	x=create(k,fr,l,r,vir);
       	if(col[fa[x]]&&!vir)fix(x,vir);
       	if(col[rt]==1)
       	   {
       	   if(vir)copy(rt);
		   col[rt]=0,pushup(rt);
	       }
		if(!vir)upall(x,0,0);
		if(vir)
		   {
		   	col[cnt]=1,pushupl(cnt,0),upall(x,1,1);
		   	ou+=ans[rt];
		   	if(col[fa[x]])fix(x,vir);
		   }
       	return;
	   }
	if(vir)copy(x);
	if(k<=val[x])r=min(r,val[x]),pr[x]=max(pr[x],k),insert(ch[x][0],x,k,l,r,vir);
	else l=max(l,val[x]),nxt[x]=min(nxt[x],k),insert(ch[x][1],x,k,l,r,vir);
	pushup(x);
}

void rollback()
{
	rt=trt,cnt--;
	while(top)
	   {
	   	int x=st[top];
	   	top--,vis[x]=0,ch[x][0]=tch[x][0],ch[x][1]=tch[x][1],fa[x]=tfa[x],val[x]=tval[x],col[x]=tcol[x],ans[x]=tans[x],chgl[x]=tchgl[x],chgr[x]=tchgr[x],pr[x]=tpr[x],nxt[x]=tnxt[x];
	   }
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	    {
	    	scanf("%d%d",&op,&x);
	    	if(op==1)insert(rt,0,x,0,1e8+1,0);
	    	else trt=rt,insert(rt,0,x,0,1e8+1,1),printf("%d\n",ou),ou=0,rollback();
		}
	return 0;
}
posted @ 2025-08-06 18:50  w9095  阅读(6)  评论(0)    收藏  举报