[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);
}
發現總共三步:
- 先將 \(x\) 旋到\(Splay\)根。
- 將 \(x\) 的右兒子變虛
- 將\(x\) 的虛兒子\(y\) 變實。
於是,對應的,設\(x\)的右兒子的樹層數最小的點為z,在原樹上,\(z\)的層數為\(x\)的層數\(+1\),由於\(x-z\)這條邊由實變虛,所以\(z\)
但是由於需要用到找根操作,在討論中,許多人都在討論找根不\(splay\)時間複雜度是假的,事實上,由於找根在\(access\)中,所以\(splay\)了也是假的,所以採用討論中的做法,額外用\(c\)記錄層數最小的點即可。
\(2\)操作,我們設\(val(x)\)為\(x\)到根節點所經過的虛鏈個數\(+1\),那麼答案就是\(val(x)+val(y)-2*val(lca(x,y))+1\),至於為什麼要\(+1\),因為這樣子打會沒有算上\(lca\)的顏色,所以要\(+1\),而且由於\(val\)
而\(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;
}