合理的正解大概是动态点分治,这里给出其实现

  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的更多相关文章

  1. QTREE5 - Query on a tree V——LCT

    QTREE5 - Query on a tree V 动态点分治和动态边分治用Qtree4的做法即可. LCT: 换根后,求子树最浅的白点深度. 但是也可以不换根.类似平常换根的往上g,往下f的拼凑 ...

  2. SPOJ Query on a tree V

    You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are number ...

  3. 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 ...

  4. 2019.02.17 spoj Query on a tree V(链分治)

    传送门 题意简述: 给你一棵nnn个黑白点的树,初始全是黑点. 现在支持给一个点换颜色或者求整颗树中离某个点最近的白点跟这个点的距离. 思路: 考虑链分治维护答案,每个链顶用一个堆来维护答案,然后对于 ...

  5. QTREE5 - Query on a tree V(LCT)

    题意翻译 你被给定一棵n个点的树,点从1到n编号.每个点可能有两种颜色:黑或白.我们定义dist(a,b)为点a至点b路径上的边个数. 一开始所有的点都是黑色的. 要求作以下操作: 0 i 将点i的颜 ...

  6. SPOJ QTREE Query on a tree V ——动态点分治

    [题目分析] QTREE4的弱化版本 建立出分治树,每个节点的堆表示到改点的最近白点距离. 然后分治树上一直向上,取min即可. 正确性显然,不用担心出现在同一子树的情况(不会是最优解),请自行脑补. ...

  7. SPOJ - QTREE5 Query on a tree V 边分治

    题目传送门 题意:给你一棵树, 然后树上的点都有颜色,且原来为黑,现在有2个操作,1 改变某个点的颜色, 2 询问树上的白点到u点的最短距离是多少. 题解: 这里用的还是边分治的方法. 把所有东西都抠 ...

  8. Query on a tree——树链剖分整理

    树链剖分整理 树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护. 通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中s ...

  9. SPOJ 375. Query on a tree (树链剖分)

    Query on a tree Time Limit: 5000ms Memory Limit: 262144KB   This problem will be judged on SPOJ. Ori ...

随机推荐

  1. 从源码分析node-gyp指定node库文件下载地址

    当我们安装node的C/C++原生模块时,涉及到使用node-gyp对C/C++原生模块的编译工作(configure.build).这个过程,需要nodejs的头文件以及静态库参与(后续称库文件)对 ...

  2. postgresql高可用集群部署

    一.概况 1.概念 pgsql高可用集群采用postgresql+etcd+patroni+haproxy+keepalived等软件实现,以postgresql做数据库,etcd存储集群状态,pat ...

  3. 牛逼的磁盘检查工具duf

    1.部署 wget https://github.com/muesli/duf/releases/download/v0.5.0/checksums.txt wget https://github.c ...

  4. 高中最后一刻&大学第一课&为人师的责任

    文章不是技术文,只是分享一些感想,作为一只程序猿,不说好好敲代码,跑出来思考人生,不是合格的程序猿,罪过罪过,自我反思3秒钟,我们继续,毕竟程序猿的人生不只是Coding,也希望自己这点感想被更多刚入 ...

  5. CF850E Random Elections 题解

    题目传送门 题目大意 没法描述,过于繁杂. 思路 果然自己是个菜鸡,只能靠读题解读题,难受极了,其实不是很难自己应该做得出来的....哎.... 不难发现可以统计 \(A\) 获胜的情况乘上 \(3\ ...

  6. bzoj3073Journeys(线段树优化最短路)

    这里还是一道涉及到区间连边的问题. 如果暴力去做,那么就会爆炸 那么这时候就需要线段树来优化了. 因为是双向边 所以需要两颗线段树来分别对应入边和出边 QwQ然后做就好了咯 不过需要注意的是,这个边数 ...

  7. MyBatis 中两表关联查询MYSQL (14)

    MyBatis 中两表关联查询MYSQL 1.创建数据库表语句 2.插入测试数据 3.pom文件内容 <?xml version="1.0" encoding="U ...

  8. 爬虫逆向基础,理解 JavaScript 模块化编程 webpack

    关注微信公众号:K哥爬虫,QQ交流群:808574309,持续分享爬虫进阶.JS/安卓逆向等技术干货! 简介 在分析一些站点的 JavaScript 代码时,比较简单的代码,函数通常都是一个一个的,例 ...

  9. 4.19——数组双指针——26. 删除有序数组中的重复项 & 27. 删除有序数组中的重复项II & 80. 删除有序数组中的重复项 II

    第一次做到数组双指针的题目是80: 因为python的List是可以用以下代码来删除元素的: del List[index] 所以当时的我直接用了暴力删除第三个重复元素的做法,大概代码如下: n = ...

  10. VS2015+OpenCV+Qt

    VS2015+OpenCV+Qt 01.OpenCV 下载 进入官网链接: https://opencv.org,下载所需要的版本: 下载完成后直接双击,选择解压路径,解压到响应的文件夹中: 若之后需 ...