1. 程式人生 > >【GDSOI2017】 中學生資料結構題(LCT)

【GDSOI2017】 中學生資料結構題(LCT)

Description

這裡寫圖片描述

Solution

雖然這只是Sone0裡很小的一部分,但是我認為這是最煩的一部分,對著一個錯誤的題目調了兩天TAT(最後才發現自己的程式的輪換打反了)。
如果想直接用一棵LCT來做有一個最簡單的想法,就是把x到y這段提取出來,然後再輪換一下。但是,這個想法明顯有問題,因為輪換的時候只是換了一下相對順序,並沒有交換權值的大小,就是說在下一次詢問編號的時候,原來的點x權值c,換了之後,還是會訪問到點x和權值c。這樣權值就根本沒有換過。
我們現在就發現了,編號是一個很麻煩的東西。
所以,我們可以考慮一下,權值只維護相對位置,然後再次同時我們再維護一下它的編號,這樣權值就可以和編號一一對應了。但是我們還是會發現這樣是不能詢問編號的,所以我們只能把編號打出來,再建一棵編號LCT

,因為是從前面的權值LCT打出來的,那麼這個權值LCT的中序遍歷(相對順序)和編號LCT的中序遍歷(相對順序)是一一對應的。因為要一一對應,所以我們在access的時候,兩棵LCT要一起變動,換根的時候深度標記也要一起翻轉。
因為編號LCT上的編號是不會變化的,編號和原樹是一樣的。
那麼在詢問編號x到編號y上的資訊的時候,(因為兩個LCT的相對順序是一樣的,所以access和makeroot的時候都是打在一起的,因為還要兩個一起修改樹的結構),先在樹上把編號x到y放到一起,然後再尋找此時在權值LCT上對應的點,然後當前這個點的splay與x(或y)所在的splay是一一對應的,那麼直接修改或查詢即可。
輪換的時候,知道權值的那一段,然後直接修改權值的相對順序就可以了。
看到這裡,肯定有一個很明顯的問題,怎樣在編號LCT的x節點找到對應的權值LCT的節點。
我們記錄一個g[0][x]和g[1][x]分別表示(0是權值的,1是編號的),權值LCTx對應編號LCT的根節點,編號LCTx對應權值LCT的根節點。我們知道LCT上有很多個虛邊,那麼頭上是虛邊的那個就是虛邊下面splay的根節點,所以我們記錄這些根節點一一對應,不是根節點的點沒有g值。所以g要在access和輪換和rotate的時候更新。
找到對應的根節點之後就很好辦了,因為找出來的那個splay是和當前被找的那個splay相對順序一樣的,所以在另外那麼的splay找x的時候,只用找中序遍歷為size[t[0][x]]+1(及x在0【或1】樹上的中序遍歷)的節點即可。
維護虛邊的時候其實只用打一個數組,因為只有編號對應的LCT才是和原樹的編號對應的。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
int i,j,k,l,n,m,ans,p,q,u;
int t[2
][maxn][2],pfa[maxn],f[2][maxn],bz[2][maxn],d[maxn]; int first[maxn],last[maxn],next[maxn],num,x,y,g[2][maxn]; ll sum[2][maxn],ad[2][maxn],a[2][maxn],size[2][maxn],z; char s[9]; void add(int x,int y){ last[++num]=y,next[num]=first[x],first[x]=num; } int son(int o,int x){return(t[o][f[o][x]][0]!=x);} void update(int o,int x){ sum[o][0]=size[o][0]=0; sum[o][x]=a[o][x]+sum[o][t[o][x][0]]+sum[o][t[o][x][1]]; size[o][x]=1+size[o][t[o][x][0]]+size[o][t[o][x][1]]; } void gao(int o,int x,ll y){ sum[o][x]+=size[o][x]*y;a[o][x]+=y;ad[o][x]+=y; } void down(int o,int x){ if(bz[o][x]){ swap(t[o][x][0],t[o][x][1]); bz[o][t[o][x][0]]^=1,bz[o][t[o][x][1]]^=1;bz[o][x]=0; } if(ad[o][x]){ gao(o,t[o][x][0],ad[o][x]),gao(o,t[o][x][1],ad[o][x]);ad[o][x]=0; } } void remove(int o,int x,int y){ while(x!=y)d[++d[0]]=x,x=f[o][x]; while(d[0])down(o,d[d[0]--]); } void rotate(int o,int x){ int y=f[o][x],z=son(o,x),u=g[o][y]; f[o][t[o][y][z]=t[o][x][1-z]]=y; t[o][f[o][x]=f[o][y]][son(o,y)]=x; if(!f[o][x]&&o)pfa[x]=pfa[y],pfa[y]=0; if(u)g[1-o][u]=x,g[o][x]=u,g[o][y]=0; f[o][t[o][x][1-z]=y]=x; update(o,y),update(o,x); } void splay(int o,int x,int y){ remove(o,x,y); while(f[o][x]!=y){ if(f[o][f[o][x]]!=y)if(son(o,f[o][x])==son(o,x))rotate(o,f[o][x]);else rotate(o,x); rotate(o,x); } } int kth(int o,int x,int k){ down(o,x);down(1-o,x);if(size[o][t[o][x][0]]+1==k)return x; if(size[o][t[o][x][0]]+1>k)return kth(o,t[o][x][0],k);return kth(o,t[o][x][1],k-size[o][t[o][x][0]]-1); } int find(int x){ splay(0,g[1][x],0); return kth(0,g[1][x],size[1][t[1][x][0]]+1); } void access(int x){ int y=0,yy=0,u,p,q; while(x){ splay(1,x,0); u=find(x);splay(0,u,0); p=t[1][x][1];f[1][p]=0;q=t[0][u][1];f[0][q]=0; pfa[p]=x;g[0][g[1][p]=q]=p;g[0][g[1][x]=u]=x; t[1][f[1][y]=x][1]=y,t[0][f[0][yy]=u][1]=yy,pfa[y]=g[1][y]=g[0][yy]=0; update(1,x),update(0,u); y=x,x=pfa[x];yy=u; } } void makeroot(int x){ int u;access(x),splay(1,x,0);u=find(x);splay(0,u,0);bz[1][x]^=1;bz[0][u]^=1; } void dfs(int x,int y){ int i; pfa[x]=y;size[0][x]=size[1][x]=1;g[0][x]=g[1][x]=x; rep(i,x)if(last[i]!=y)dfs(last[i],x); } int main(){ freopen("shift.in","r",stdin); freopen("shift.out","w",stdout); scanf("%d",&n); fo(i,1,n-1){ scanf("%d%d",&x,&y),add(x,y),add(y,x); } dfs(1,0); for(scanf("%d",&m);m;m--){ scanf("%s%d%d",s,&x,&u); makeroot(x);access(u);splay(1,u,0); y=find(u);splay(0,y,0); if(s[0]=='A'){ scanf("%lld",&z); gao(0,y,z); } else if(s[0]=='Q')printf("%lld\n",sum[0][y]); else{ p=kth(0,y,size[0][y]),q=kth(0,y,1); if(p==q)continue; splay(0,p,0);f[0][t[0][p][0]]=0,t[0][p][0]=0; splay(0,q,0);f[0][t[0][p][1]=q]=p;g[1][u]=p; update(0,q),update(0,p); } } }