[spojQTREE5]Query on a tree V
合理的正解大概是动态点分治,这里给出其实现
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 struct Edge{ 5 int nex,to; 6 }edge[N<<1]; 7 multiset<int>S[N],mnS[N]; 8 int n,m,E,x,y,head[N],fa[N],vis[N]; 9 void add(int x,int y){ 10 edge[E].nex=head[x]; 11 edge[E].to=y; 12 head[x]=E++; 13 } 14 namespace DIST{ 15 int dep[N],f[N][20]; 16 void dfs(int k,int fa,int s){ 17 f[k][0]=fa,dep[k]=s; 18 for(int i=1;i<20;i++)f[k][i]=f[f[k][i-1]][i-1]; 19 for(int i=head[k];i!=-1;i=edge[i].nex) 20 if (edge[i].to!=fa)dfs(edge[i].to,k,s+1); 21 } 22 int lca(int x,int y){ 23 if (dep[x]<dep[y])swap(x,y); 24 for(int i=19;i>=0;i--) 25 if (dep[f[x][i]]>=dep[y])x=f[x][i]; 26 if (x==y)return x; 27 for(int i=19;i>=0;i--) 28 if (f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 29 return f[x][0]; 30 } 31 int dis(int x,int y){ 32 return dep[x]+dep[y]-(dep[lca(x,y)]<<1); 33 } 34 }; 35 namespace DIVIDE{ 36 int rt,sz[N],vis[N]; 37 void get_sz(int k,int fa){ 38 sz[k]=1; 39 for(int i=head[k];i!=-1;i=edge[i].nex) 40 if ((!vis[edge[i].to])&&(edge[i].to!=fa)){ 41 get_sz(edge[i].to,k); 42 sz[k]+=sz[edge[i].to]; 43 } 44 } 45 void get_rt(int k,int fa,int s){ 46 int mx=s-sz[k]; 47 for(int i=head[k];i!=-1;i=edge[i].nex) 48 if ((!vis[edge[i].to])&&(edge[i].to!=fa)){ 49 get_rt(edge[i].to,k,s); 50 mx=max(mx,sz[edge[i].to]); 51 } 52 if (mx<=(s>>1))rt=k; 53 } 54 int dfs(int k){ 55 get_sz(k,0); 56 get_rt(k,0,sz[k]); 57 k=rt,vis[k]=1; 58 for(int i=head[k];i!=-1;i=edge[i].nex) 59 if (!vis[edge[i].to])fa[dfs(edge[i].to)]=k; 60 return k; 61 } 62 }; 63 void add(int k){ 64 if (!S[k].empty())mnS[fa[k]].insert(*S[k].begin()); 65 } 66 void del(int k){ 67 if (!S[k].empty())mnS[fa[k]].erase(mnS[fa[k]].find(*S[k].begin())); 68 } 69 void Add(int k){ 70 mnS[k].insert(0); 71 for(int i=k;fa[i];i=fa[i]){ 72 del(i); 73 S[i].insert(DIST::dis(k,fa[i])); 74 add(i); 75 } 76 } 77 void Del(int k){ 78 mnS[k].erase(mnS[k].find(0)); 79 for(int i=k;fa[i];i=fa[i]){ 80 del(i); 81 S[i].erase(S[i].find(DIST::dis(k,fa[i]))); 82 add(i); 83 } 84 } 85 void update(int k){ 86 if (vis[k])Del(k); 87 vis[k]^=1; 88 if (vis[k])Add(k); 89 } 90 int query(int k){ 91 int ans=0x3f3f3f3f; 92 if (mnS[k].size())ans=(*mnS[k].begin()); 93 for(int i=k;fa[i];i=fa[i]){ 94 del(i); 95 if (!mnS[fa[i]].empty())ans=min(ans,(*mnS[fa[i]].begin())+DIST::dis(k,fa[i])); 96 add(i); 97 } 98 if (ans==0x3f3f3f3f)ans=-1; 99 return ans; 100 } 101 int main(){ 102 scanf("%d",&n); 103 memset(head,-1,sizeof(head)); 104 for(int i=1;i<n;i++){ 105 scanf("%d%d",&x,&y); 106 add(x,y),add(y,x); 107 } 108 DIST::dfs(1,0,0); 109 DIVIDE::dfs(1); 110 scanf("%d",&m); 111 for(int i=1;i<=m;i++){ 112 scanf("%d%d",&x,&y); 113 if (!x)update(y); 114 else printf("%d\n",query(y)); 115 } 116 return 0; 117 }
下面,来考虑LCT的做法:
与求最小生成树的LCT相同,将边拆成点,并将边权变为点权
为了方便,这里给出一些定义:
1.称一个点对应的实链为Splay上其子树内所有点(显然这些点构成一段实链)
2.称一个点的子树范围为自身$\cup $其Splay上左右儿子的子树范围$\cup $所有虚儿子的子数范围
(也可以理解为原树中对应的实链上所有节点虚儿子子树的并)
对每一个节点,维护以下信息:
1.对应的实链点权和(即Splay上子树点权和)
2.其子树范围内到其对应的实链链顶/链尾距离最小的白点(的距离)
3.其自身和每一个虚儿子的子树范围内到其距离最小的白点(的距离)所构成的集合(距离不包括其自身的权值,这是为了方便修改权值)
通过这些信息,只需要将查询点make_root到根,那么此时整个Splay的根(并不是原树的根)的第2个信息即为答案(但由于可能找不到该位置,不妨再splay(x)一下)
下面,问题即如何维护这些信息,实际上LCT的信息维护基本都只需要考虑以下两种变化:
1.修改了Splay上子树的信息(即重新up),那么其中第1个信息容易维护,第3个信息没有影响,下面来考虑如何维护第2个信息(以链顶为例)——
将其子树范围分为三类:
(1)Splay上左儿子的子树范围,这个即为左儿子的该信息
(2)Splay上右儿子的子树范围,考虑其对应的实链,即右儿子先走到自己对应的链链顶,再从其通过左儿子对应的整条实链即可,那么即右儿子的该信息+其点权+左儿子的第1个信息
(3)自身$\cup $虚儿子的子树范围,同样要先到达其,即第3个信息维护的集合中最小值+左儿子的第1个信息
2.增加/删除了某个虚儿子(不维护子树信息的LCT对此无影响),此时即要求该虚儿子子树范围内到其距离最小的点(的距离),将虚儿子的第2个信息(链顶)+其点权在集合中加入或删除即可
综上,时间复杂度为$o(n\log^{2}n)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 200005 4 multiset<int>S[N]; 5 int n,m,x,y,vis[N],st[N],fa[N],tag[N],val[N],sum[N],ch[N][2],f[N][2]; 6 bool check(int k){ 7 return (ch[fa[k]][0]!=k)&&(ch[fa[k]][1]!=k); 8 } 9 int which(int k){ 10 return ch[fa[k]][1]==k; 11 } 12 void rev(int k){ 13 tag[k]^=1; 14 swap(ch[k][0],ch[k][1]); 15 swap(f[k][0],f[k][1]); 16 } 17 void add_vir(int k){ 18 S[fa[k]].insert(f[k][0]); 19 } 20 void del_vir(int k){ 21 S[fa[k]].erase(S[fa[k]].find(f[k][0])); 22 } 23 int get_min(int k){ 24 if (S[k].empty())return 0x3f3f3f3f; 25 return (*S[k].begin()); 26 } 27 void up(int k){ 28 sum[k]=sum[ch[k][0]]+sum[ch[k][1]]+val[k]; 29 f[k][0]=min(f[ch[k][0]][0],min(get_min(k),f[ch[k][1]][0])+val[k]+sum[ch[k][0]]); 30 f[k][1]=min(f[ch[k][1]][1],min(get_min(k),f[ch[k][0]][1])+val[k]+sum[ch[k][1]]); 31 } 32 void down(int k){ 33 if (tag[k]){ 34 if (ch[k][0])rev(ch[k][0]); 35 if (ch[k][1])rev(ch[k][1]); 36 tag[k]=0; 37 } 38 } 39 void rotate(int k){ 40 int f=fa[k],g=fa[f],p=which(k); 41 fa[k]=g; 42 if (!check(f))ch[g][which(f)]=k; 43 fa[ch[k][p^1]]=f,ch[f][p]=ch[k][p^1]; 44 fa[f]=k,ch[k][p^1]=f; 45 up(f),up(k); 46 } 47 void splay(int k){ 48 for(int i=k;;i=fa[i]){ 49 st[++st[0]]=i; 50 if (check(i))break; 51 } 52 while (st[0])down(st[st[0]--]); 53 for(int i=fa[k];!check(k);i=fa[k]){ 54 if (!check(i)){ 55 if (which(i)==which(k))rotate(i); 56 else rotate(k); 57 } 58 rotate(k); 59 } 60 } 61 void access(int k){ 62 int lst=0; 63 while (k){ 64 splay(k); 65 if (ch[k][1])add_vir(ch[k][1]); 66 if (lst)del_vir(lst); 67 ch[k][1]=lst,up(k); 68 lst=k,k=fa[k]; 69 } 70 } 71 void make_root(int k){ 72 access(k); 73 splay(k); 74 rev(k); 75 } 76 void add(int x,int y){ 77 make_root(x); 78 make_root(y); 79 fa[y]=x,add_vir(y),up(x); 80 } 81 void upd_val(int k,int x){ 82 make_root(k); 83 val[k]=x,up(k); 84 } 85 void upd_col(int k){ 86 make_root(k); 87 if (vis[k])S[k].erase(S[k].find(0)); 88 vis[k]^=1; 89 if (vis[k])S[k].insert(0); 90 up(k); 91 } 92 int query(int k){ 93 make_root(k); 94 if (f[k][0]==0x3f3f3f3f)return -1; 95 return f[k][0]; 96 } 97 int main(){ 98 scanf("%d",&n); 99 memset(f,0x3f,sizeof(f)); 100 for(int i=1;i<n;i++){ 101 scanf("%d%d",&x,&y); 102 add(x,i+n),add(y,i+n); 103 upd_val(i+n,1); 104 } 105 scanf("%d",&m); 106 for(int i=1;i<=m;i++){ 107 scanf("%d%d",&x,&y); 108 if (!x)upd_col(y); 109 else printf("%d\n",query(y)); 110 } 111 return 0; 112 }