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

  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. windows下编译caffe出现错误 C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe?

    解决方案来自http://blog.csdn.net/u012556077/article/details/50353818

  2. 题解 [ZJOI2019]语言

    题目传送门 题目大意 给出一个 \(n\) 个点的树,现在有 \(m\) 次操作,每次可以选择一个链 \(s,t\),,然后这条链上每个点都会增加一个相同属性,问对于每一个点有与它相同属性的有多少个点 ...

  3. 初识Linux shell

    目录 初识Linux shell Linux 深入探究Linux内核 系统内存管理 交换空间 页面 换出 软件程序管理 Linux中的进程 Linux系统的运行级 硬件设备管理 插入设备驱动代码的方法 ...

  4. JSR303数据校验

    Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理. 导入依赖: <dependency> <groupId>org ...

  5. pytest执行时mian函数传参

    在代码中执行pytest可以通过main函数 加参数来指定运行规则时,参数需要放在列表或者元祖中 # pytest.main(["--html=report.html"]) # p ...

  6. 2020.12.14--Codeforces Round #104 (Div.2)补题

    C - Lucky Conversion CodeForces - 146C Petya loves lucky numbers very much. Everybody knows that luc ...

  7. C++ 类继承 笔记(初步)

    本节内容源于对C++ primer第13章的学习,这本书把C++的原理将得明明白白.网上的博客往往讲得一头雾水.到头来还不如看原书本. 问题 首先给出一题: #include<stdio.h&g ...

  8. 微服务网关Ocelot加入IdentityServer4鉴权-.NetCore(.NET5)中使用

    Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Consul服务注册,服务发现 Consul+Ocelot+Polly在.NetCore中使用(.NET5)-网关Ocel ...

  9. 网页常用的css特效让互动留住客户

    一般网站如果制作按钮,多做一些互动,可以让客户获得更好的体验. 例如鼠标滑过按钮,让背景颜色从左往右滑出来(或者从右往左都可以): <a target="_blank" hr ...

  10. 华为在HDC2021发布全新HMS Core 6 宣布跨OS能力开放

    [2021年10月22日·东莞]华为开发者大会 2021(Together)于今天正式开幕,华为在主题演讲中正式发布全新的HMS Core 6,向全球开发者开放7大领域的69个Kit和21,738个A ...