Query on a tree V
You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are numbered from 1 to N. We define dist(a, b) as the number of edges on the path from node a to node b.
Each node has a color, white or black. All the nodes are black initially.
We will ask you to perfrom some instructions of the following form:
- 0 i : change the color of i-th node(from black to white, or from white to black).
- 1 v : ask for the minimum dist(u, v), node u must be white(u can be equal to v). Obviously, as long as node v is white, the result will always be 0.
Input
- In the first line there is an integer N (N <= 100000)
- In the next N-1 lines, the i-th line describes the i-th edge: a line with two integers a b denotes an edge between a and b.
- In the next line, there is an integer Q denotes the number of instructions (Q <= 100000)
- In the next Q lines, each line contains an instruction "0 i" or "1 v"
Output
For each "1 v" operation, print one integer representing its result. If there is no white node in the tree, you should write "-1".
Example
Input:
10
1 2
1 3
2 4
1 5
1 6
4 7
7 8
5 9
1 10
10
0 6
0 6
0 6
1 3
0 1
0 1
1 3
1 10
1 4
1 6
Output:
2
2
2
3
0
题意: 给出一棵n结点的树,边权为1,一开始每个点颜色都是黑色。
现有q个询问,每次询问会有两种操作:
1.0 i 改变i的颜色,黑变白,白变黑。
2.1 v 询问与v距离最近的白点,显然,当v颜色为白色时,答案是0。
题解:其实该题就是一个模板题,前置技能:动态点分治和lca
先用动态点分治存储每颗子树的重心,目的是将后面的查询与修改复杂度降到 log(n)
首先我们得为每一个点创建一个堆,我们知道每个点都是一颗子树的重心,我们找到该点为重心的子树中的所有白点,用重心对应的堆来维护这些白点到重心的距离最小值
操作0:在修改过程中,当遇到一个白点的时候,我们找到存在该点的所有子树(点分治分离出来的子树)的重心,将每个重心到该白点的距离丢到该重心对应堆里
void op0(int v){
col[v] = !col[v]; // 修改颜色
if(col[v]) k ++; // k表示白色点的数目
else k --;
if(col[v]) { // 如果修改为白色后,我们就要计算重心到该点的距离并堆维护
int cent = v; // cent表示重心
while(cent != -1) {
int comfa = lca(cent, v); // comfa 表示公共祖先
qu[cent].push ( len(v, dep[cent] + dep[v] - 2 * dep[comfa]) ); // 堆维护
cent = pre[cent]; // 递归到上一个重心
}
}
}
操作1:在查询过程中,我们同样的找到存在该点的所有子树(点分治分离出来的子树)的重心,我们将该点到重心的距离算出来,再加上距离该重心最近的白点的距离。针对以上找到的每一个重心,我们做一次以上计算,取其中的最小值输出。当然要先特判白点的个数是否为0或者该点本身是否为白点
int op1(int v){
if(k == 0) return -1; // 特判
if(col[v]) return 0;
int ans = INF;
int cent = v; // cent表示重心
while(cent != -1) {
int comfa = lca(v, cent); // comfa 表示公共祖先
int dis = get_dis(cent); // dis 表示到cent最近的白点的距离
ans = min(ans, dep[cent] + dep[v] - 2 * dep[comfa] + dis);
cent = pre[cent]; // 递归到上一个重心
}
return ans;
}
关于堆维与get_dis() 函数,如果要维护的点的颜色时候已经修改为黑色,我们需要把它删除,所以我们在维护距离的时候同时要存下点的位置
struct len { int to, dis;
len (int x, int y) : to(x), dis(y) {};
bool friend operator < (len a, len b) { return a.dis > b.dis; }
// 维护最小的dis
};
priority_queue< len >qu[maxn];
int get_dis(int u) {
while(!qu[u].empty() && !col[ qu[u].top().to]) qu[u].pop();
// 若已经修改为黑色需要pop一下
return qu[u].empty() ? INF : qu[u].top().dis;
}
下面附上AC代码
//#include<bits/stdc++.h>
//#include<unordered_map>
//#include<unordered_set>
#include<iostream>
#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a, b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f;
const int O = 1e6;
const int mod = 1e6+7;
const int maxn = 4e5 +5;
const double PI = acos(-1.0);
const double E = 2.718281828459;
const double eps = 1e-8;
struct dd { int to, next;
dd () {};
dd (int x, int y) : to(x), next(y) {};
}e[maxn];
int head[maxn], cnt = 0;
void add(int u, int v) { e[cnt] = dd (v, head[u]); head[u] = cnt ++; }
int n, k = 0;
// 点分
int root, max_son[maxn], sum[maxn], vis[maxn], pre[maxn];
void get_root(int u, int fa, int num) {
sum[u] = 1; max_son[u] = 0;
for(int i=head[u]; i!=-1; i=e[i].next) {
int v = e[i].to;
if(v == fa || vis[v]) continue;
get_root(v, u, num);
sum[u] += sum[v];
max_son[u] = max(max_son[u], sum[v]);
}
max_son[u] = max(max_son[u], num - sum[u]);
if(max_son[u] < max_son[root]) root = u;
}
void fun(int u, int f) {
pre[u] = f; vis[root] = 1;
for(int i=head[root]; i!=-1; i=e[i].next) {
int v = e[i].to;
if(vis[v]) continue;
max_son[root = 0] = INF; get_root(v, u, sum[v]);
fun(root, u);
}
}
// LCA
int dep[maxn], up[maxn][21];
void dfs(int u,int fa,int d) {
dep[u] = d + 1;
for(int i = 1 ; i < 20 ; i ++) {
up[u][i] = up[up[u][i-1]][i-1];
}
for(int i = head[u] ; i!=-1 ; i = e[i].next) {
int v = e[i].to;
if(v == fa) continue;
up[v][0] = u;
dfs(v, u, d+1);
}
}
int lca(int u,int v) {
int mx = 0;
if(dep[u] < dep[v]) swap(u,v);
int k = dep[u] - dep[v];
for(int i = 19 ; i >= 0 ; i --) {
if((1<<i) & k) {
u = up[u][i];
}
}
if(u == v) return u;
for(int i = 19 ; i >= 0 ; i --) {
if(up[u][i] != up[v][i]){
u = up[u][i];
v = up[v][i];
}
}
return up[u][0];
}
// 堆维
int col[maxn];
struct len { int to, dis;
len (int x, int y) : to(x), dis(y) {};
bool friend operator < (len a, len b) { return a.dis > b.dis; }
};
priority_queue< len >qu[maxn];
int get_dis(int u) {
while(!qu[u].empty() && !col[ qu[u].top().to]) qu[u].pop();
return qu[u].empty() ? INF : qu[u].top().dis;
}
void op0(int v){
col[v] = !col[v];
if(col[v]) k ++;
else k --;
if(col[v]) {
int cent = v;
while(cent != -1) {
int comfa = lca(cent, v);
qu[cent].push ( len(v, dep[cent] + dep[v] - 2 * dep[comfa]) );
cent = pre[cent];
}
}
}
int op1(int v){
if(k == 0) return -1;
if(col[v]) return 0;
int ans = INF;
int cent = v;
while(cent != -1) {
int comfa = lca(v, cent);
int dis = get_dis(cent);
ans = min(ans, dep[cent] + dep[v] - 2 * dep[comfa] + dis);
cent = pre[cent];
}
return ans;
}
int main(){
scanf("%d", &n);
MT(head, -1);
for(int i=1; i<n; i++) {
int u, v; scanf("%d%d", &u, &v);
add(u, v); add(v, u);
}
dfs(1, -1, 0);
max_son[root = 0] = INF; get_root(1, -1, n);
fun(root, -1);
int q; scanf("%d", &q);
while( q --) {
int c, v; scanf("%d%d", &c, &v);
if(c) printf("%d\n", op1(v));
else op0(v);
}
return 0;
}