1. 程式人生 > 實用技巧 >[SDOI2017]樹點塗色(LCT)

[SDOI2017]樹點塗色(LCT)

題目

題目

參考

題解:
https://www.luogu.com.cn/blog/Soulist/solution-p3703
討論:
https://www.luogu.com.cn/discuss/show/209134
https://www.luogu.com.cn/discuss/show/192352

做法

妙啊。

這道題目。

妙啊。

考慮用LCT。

我們在LCT,同色的聯通塊就在同一個Splay上(很明顯每個同色聯通塊是一條鏈),而對於一個點到根節點的權,其實就是走到根節點的過程中,走過虛鏈的個數+1,所以不妨用\(DFS\)序+線段樹維護每個點到根節點的虛鏈個數。

我們發現\(1\)操作其實就是LCT的\(access\)

操作,但是對應的,樹的結構也發生了改變,所以需要重新計算虛鏈個數。

通過觀察普通\(ace\)的過程:

void access( int x ) {
	for( int y = 0; x; y = x, x = t[y].fa )
    	Splay(x), t[x].son[1] = y, pushup(x);
}

發現總共三步:

  1. 先將 \(x\) 旋到\(Splay\)根。
  2. \(x\) 的右兒子變虛
  3. \(x\) 的虛兒子\(y\) 變實。

於是,對應的,設\(x\)的右兒子的樹層數最小的點為z,在原樹上,\(z\)的層數為\(x\)的層數\(+1\),由於\(x-z\)這條邊由實變虛,所以\(z\)

的子樹到根節點需要多走一條虛邊,對應線上段樹上整體\(+1\),而第三步由虛變實,則是\(-1\)

但是由於需要用到找根操作,在討論中,許多人都在討論找根不\(splay\)時間複雜度是假的,事實上,由於找根在\(access\)中,所以\(splay\)了也是假的,所以採用討論中的做法,額外用\(c\)記錄層數最小的點即可。

\(2\)操作,我們設\(val(x)\)\(x\)到根節點所經過的虛鏈個數\(+1\),那麼答案就是\(val(x)+val(y)-2*val(lca(x,y))+1\),至於為什麼要\(+1\),因為這樣子打會沒有算上\(lca\)的顏色,所以要\(+1\),而且由於\(val\)

函式中的\(+1\)會前後抵消,所以我程式碼中的\(val\)函式並沒有\(+1\)

\(3\)操作則更加簡單,直接在對應子樹找最大即可。

樹鏈剖分的話,實際上是模擬\(access\),他們每次找到\(x\)所在的顏色,和顏色對應的一段,在這一段上進行跳重鏈,實際上是對應了\(access\)中的一次\(splay\)操作,然後取消右兒子的操作,但是由於\(access\)操作總共會跳最多\(m\log{n}\)次的\(splay\)(這裡我的計算方法是\(LCT\)時間複雜度是\(m\log{n}\)的,假設每次\(splay\)都只有一次\(rotate\),但實際上這種演算法及其不靠譜,但至少不會超過這個上界),而每一個\(splay\)對應著一段顏色,用樹剖跳一段顏色是\(O(log^2n)\)的,那麼應該是\(O(mlog^3n)\)

但是我的分析方法太暴力了,又沒有均攤,可能真的就是他們說的O(nlog^2n)吧,大概吧,希望有大佬知道告知一下。

程式碼

#include<cstdio>
#include<cstring>
#define  N  110000
#define  NN  210000
using  namespace  std;
inline  int  mymax(int  x,int  y){return  x>y?x:y;}
inline  void  zwap(int  &x,int  &y){x^=y^=x^=y;}
int  val[N];
int  n,m;
namespace  A//線段樹
{
	struct  node
	{
		int  l,r,c,lazy;
	}tr[NN];int  len;
	inline  void  updata(int  x){tr[x].c=mymax(tr[tr[x].l].c,tr[tr[x].r].c);}
	inline  void  pushlazy(int  x,int  d){tr[x].c+=d;tr[x].lazy+=d;}
	inline  void  downdata(int  x)
	{
		if(tr[x].lazy)
		{
			pushlazy(tr[x].l,tr[x].lazy);pushlazy(tr[x].r,tr[x].lazy);
			tr[x].lazy=0;
		}
	}
	inline  void  bt(int  l,int  r)
	{
		int  now=++len;
		if(l==r)tr[now].c=val[l];
		else
		{
			int  mid=(l+r)>>1;
			tr[now].l=len+1;bt(l,mid);
			tr[now].r=len+1;bt(mid+1,r);
			updata(now);
		}
	}
	void  change(int  now,int  l,int  r,int  ll,int  rr,int  k)
	{
		if(l==ll  &&  r==rr){pushlazy(now,k);return  ;}
		int  mid=(l+r)>>1;
		downdata(now);
		if(rr<=mid)change(tr[now].l,l,mid,ll,rr,k);
		else  if(mid<ll)change(tr[now].r,mid+1,r,ll,rr,k);
		else  change(tr[now].l,l,mid,ll,mid,k),change(tr[now].r,mid+1,r,mid+1,rr,k);
		updata(now);
	} 
	int  findans(int  now,int  l,int  r,int  ll,int  rr)
	{
		if(l==ll  &&  r==rr)return  tr[now].c;
		int  mid=(l+r)>>1;
		downdata(now);
		if(rr<=mid)return  findans(tr[now].l,l,mid,ll,rr);
		else  if(mid<ll)return  findans(tr[now].r,mid+1,r,ll,rr);
		else  return  mymax(findans(tr[now].l,l,mid,ll,mid),findans(tr[now].r,mid+1,r,mid+1,rr));
	}
}
namespace  B//Splay
{
	struct  node
	{
		int  son[2],f,c;
	}tr[N];
	inline  bool  nroot(int  x){return  !(tr[tr[x].f].son[0]==x  ||  tr[tr[x].f].son[1]==x);}
	inline  int  pd_son(int  x){return  tr[tr[x].f].son[0]==x?0:1;}
	inline  void  updata(int  x){!tr[x].son[0]?tr[x].c=x:tr[x].c=tr[tr[x].son[0]].c;}
	inline  void  rotate(int  x)
	{
		int  s=pd_son(x),f=tr[x].f,ff=tr[f].f;
		tr[x].f=ff;if(!nroot(f)/*他不是根*/)tr[ff].son[pd_son(f)]=x;
		tr[f].son[s]=tr[x].son[s^1];if(tr[x].son[s^1])tr[tr[x].son[s^1]].f=f;
		tr[x].son[s^1]=f;tr[f].f=x;
		updata(f);updata(x);updata(ff);
	}
	int  sta[N],top;
	void  splay(int  x)
	{
		while(!nroot(x))
		{
			int  f=tr[x].f;
			if(nroot(f)){rotate(x);break;}
			if(pd_son(f)==pd_son(x))rotate(f);
			else  rotate(x);
			rotate(x);
		}
	}
}
struct  node
{
	int  y,next;
}a[NN];int  len,last[N];
inline  void  ins(int  x,int  y){len++;a[len].y=y;a[len].next=last[x];last[x]=len;}
int  dfn[N],dl[N],dr[N],ti;
int  fa[N][20],dep[N];
void  dfs(int  x)
{
	for(int  i=1;i<=16;i++)
	{
		fa[x][i]=fa[fa[x][i-1]][i-1];
		if(!fa[x][i])break;
	}
	dfn[x]=++ti;val[ti]=dep[x];dl[x]=ti;
	for(int  k=last[x];k;k=a[k].next)
	{
		int  y=a[k].y;
		if(!dfn[y])fa[y][0]=x,B::tr[y].f=x,dep[y]=dep[x]+1,dfs(y);
	}
	dr[x]=ti;
}
inline  int  lca(int  x,int  y)
{
	if(dep[x]>dep[y])x^=y^=x^=y;
	for(int  i=16;i>=0;i--)
	{
		if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
	}
	if(x==y)return  x;
	for(int  i=16;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
	}
	return  fa[x][0];
}
void  ace(int  x)
{
	for(int  y=0;x;x=B::tr[y=x].f)
	{
		B::splay(x);
		if(B::tr[x].son[1])
		{
			int  rt=B::tr[B::tr[x].son[1]].c;
			A::change(1,1,n,dl[rt],dr[rt],1);
		}
		if(B::tr[x].son[1]=y)
		{
			int  rt=B::tr[y].c;
			A::change(1,1,n,dl[rt],dr[rt],-1);
		}
	}
}
int  main()
{
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	dep[0]=-1;
	scanf("%d%d",&n,&m);
	for(int  i=1;i<n;i++)
	{
		int  x,y;scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x);
	}
	dfs(1);//一個DFS處理了一萬個東西 
	A::bt(1,n);
	for(int  i=1;i<=n;i++)B::tr[i].c=i;
	for(int  i=1;i<=m;i++)
	{
		int  type,x,y;
		scanf("%d",&type);
		if(type==1)
		{
			scanf("%d",&x);
			ace(x);
		}
		else  if(type==2)
		{
			scanf("%d%d",&x,&y);
			int  z=lca(x,y);
			printf("%d\n",A::findans(1,1,n,dfn[x],dfn[x])+A::findans(1,1,n,dfn[y],dfn[y])-2*A::findans(1,1,n,dfn[z],dfn[z])+1);
		}
		else
		{
			scanf("%d",&x);
			printf("%d\n",A::findans(1,1,n,dl[x],dr[x])+1);
		}
	}
	return  0;
}