[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 }
[spojQTREE5]Query on a tree V的更多相关文章
- QTREE5 - Query on a tree V——LCT
QTREE5 - Query on a tree V 动态点分治和动态边分治用Qtree4的做法即可. LCT: 换根后,求子树最浅的白点深度. 但是也可以不换根.类似平常换根的往上g,往下f的拼凑 ...
- SPOJ Query on a tree V
You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are number ...
- SPOJ QTREE Query on a tree V
You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are number ...
- 2019.02.17 spoj Query on a tree V(链分治)
传送门 题意简述: 给你一棵nnn个黑白点的树,初始全是黑点. 现在支持给一个点换颜色或者求整颗树中离某个点最近的白点跟这个点的距离. 思路: 考虑链分治维护答案,每个链顶用一个堆来维护答案,然后对于 ...
- QTREE5 - Query on a tree V(LCT)
题意翻译 你被给定一棵n个点的树,点从1到n编号.每个点可能有两种颜色:黑或白.我们定义dist(a,b)为点a至点b路径上的边个数. 一开始所有的点都是黑色的. 要求作以下操作: 0 i 将点i的颜 ...
- SPOJ QTREE Query on a tree V ——动态点分治
[题目分析] QTREE4的弱化版本 建立出分治树,每个节点的堆表示到改点的最近白点距离. 然后分治树上一直向上,取min即可. 正确性显然,不用担心出现在同一子树的情况(不会是最优解),请自行脑补. ...
- SPOJ - QTREE5 Query on a tree V 边分治
题目传送门 题意:给你一棵树, 然后树上的点都有颜色,且原来为黑,现在有2个操作,1 改变某个点的颜色, 2 询问树上的白点到u点的最短距离是多少. 题解: 这里用的还是边分治的方法. 把所有东西都抠 ...
- Query on a tree——树链剖分整理
树链剖分整理 树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护. 通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中s ...
- SPOJ 375. Query on a tree (树链剖分)
Query on a tree Time Limit: 5000ms Memory Limit: 262144KB This problem will be judged on SPOJ. Ori ...
随机推荐
- Serverless 工程实践 | Serverless 应用优化与调试秘诀
作者|刘宇 前言:本文将以阿里云函数计算为例,提供了在线调试.本地调试等多种应用优化与调试方案. Serverless 应用调试秘诀 在应用开发过程中,或者应用开发完成,所执行结果不符合预期时,我 ...
- Java类加载器概述
Java类加载器概述 Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由Java 应用开发人员编写的. 系统提供的类加载器 引导类加载器 它用来加载 Java 的核心库,是用原生 ...
- 安全通信协议SSH应用与分析
一.实验简介: 本次实验属于安全协议应用与分析系列 二 实验环境: Windows server 2003 server windows xp 做client 三.实验目的 通过该实验了解SSH服务器 ...
- TypeScript 枚举指南
枚举是受 TypeScript 支持的数据类型.枚举允许您定义一组命名常量.使用它们可以更轻松地记录意图或创建一组不同的案例.枚举大多数用于面向对象的编程语言(如 Java 和 C#)中,现在也可以 ...
- Javascript深入之作用域与闭包
相信绝大多数同学都听过闭包这个概念,但闭包具体是什么估计很少有人能够说的很详细.说实话闭包在我们平时开发中应该是很常见的,并且在前端面试中闭包也是常见的重要考点,在学习闭包之前我们先来看看作用域与作用 ...
- 想要彻底搞懂大厂是如何实现Redis高可用的?看这篇文章就够了!(1.2W字,建议收藏)
高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间. 假设系统一直能够提供服务,我们说系统的可用性是100%.如果 ...
- 单机CentOS 安装 TiDB
目录 一.官网教程 二.安装步骤 1.下载并安装 TiUP: 2.声明一下环境变量,否则会找不到 tiup 命令 3.安装 TiUP 的 cluster 组件: 4.官方教程说,由于模拟多机部署,需要 ...
- 【UE4 C++】 射线检测 LineTrace 及 BoxTrace、SphereTrace、CapsuleTrace API
World.h 库里的 Trace API Trace模式 TraceSingle 单个结果 TraceMulti 多个结果 Trace 的检测依据 ByChanne ByObjectType ByP ...
- SpringCloud-初见
目录 前言 微服务概述 微服务与微服务架构 微服务优缺点 微服务技术栈 为什么选择SpringCloud作为微服务架构 SpringCloud入门 SpringCloud和SpringBoot的关系 ...
- the Agiles Scrum Meeting 3
会议时间:2020.4.11 21:30 1.每个人的工作 今天已完成的工作 yjy:基本实现广播功能的前端 issues:小组任务1-增量开发组 wjx:基本实现注销功能的后端 issues:小组任 ...