4048 Red Black Tree(ACM-ICPC 2018 青島賽區網路預賽 B)(二分+LCA)
Red Black Tree
Time Limit: 1 Second Memory Limit: 131072 KB
BaoBao has just found a rooted tree with vertices and weighted edges in his backyard. Among the vertices, of them are red, while the others are black. The root of the tree is vertex 1 and it's a red vertex.
Let's define the cost of a red vertex to be 0, and the cost of a black vertex to be the distance between this vertex and its nearest red ancestor.
Recall that
-
The length of a path on the tree is the sum of the weights of the edges in this path.
-
The distance between two vertices is the length of the shortest path on the tree to go from one vertex to the other.
-
Vertex is the ancestor of vertex if it lies on the shortest path between vertex and the root of the tree (which is vertex 1 in this problem).
As BaoBao is bored, he decides to play games with the tree. For the -th game, BaoBao will select vertices on the tree and try to minimize the maximum cost of these vertices by changing at most one vertex on the tree to a red vertex.
Note that
-
BaoBao is free to change any vertex among all the vertices to a red vertex, NOT necessary among the vertiecs whose maximum cost he tries to minimize.
-
All the games are independent. That is to say, the tree BaoBao plays with in each game is always the initial given tree, NOT the tree modified during the last game by changing at most one vertex.
Please help BaoBao calculate the smallest possible maximum cost of the given vertices in each game after changing at most one vertex to a red vertex.
Input
There are multiple test cases. The first line of the input is an integer , indicating the number of test cases. For each test case:
The first line contains three integers , and (, ), indicating the size of the tree, the number of red vertices and the number of games.
The second line contains integers (), indicating the red vertices.
The following lines each contains three integers , and (, ), indicating an edge with weight connecting vertex and in the tree.
For the following lines, the -th line will first contain an integer (). Then integers follow (), indicating the vertices whose maximum cost BaoBao has to minimize.
It's guaranteed that the sum of in all test cases will not exceed , and the sum of in all test cases will not exceed .
Output
For each test case output lines each containing one integer, indicating the smallest possible maximum cost of the vertices given in each game after changing at most one vertex in the tree to a red vertex.
Sample Input
2
12 2 4
1 9
1 2 1
2 3 4
3 4 3
3 5 2
2 6 2
6 7 1
6 8 2
2 9 5
9 10 2
9 11 3
1 12 10
3 3 7 8
4 4 5 7 8
4 7 8 10 11
3 4 5 12
3 2 3
1 2
1 2 1
1 3 1
1 1
2 1 2
3 1 2 3
Sample Output
4
5
3
8
0
0
0
Hint
The first sample test case is shown above. Let's denote as the cost of vertex .
For the 1st game, the best choice is to make vertex 2 red, so that , and . So the answer is 4.
For the 2nd game, the best choice is to make vertex 3 red, so that , , and . So the answer is 5.
For the 3rd game, the best choice is to make vertex 6 red, so that , , and . So the answer is 3.
For the 4th game, the best choice is to make vertex 12 red, so that , and . So the answer is 8.
Due to the design restrictions of ZOJ, we can only provide a subset of the testing data here (the original data is too large for ZOJ to store). We will update the testing data once we update ZOJ. Sorry for the inconvenience caused.
Author: WENG, CaizhiSource: The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online
題意:給一棵黑白樹,定義白節點代價為 0,黑節點代價為到最近的白節點祖先的距離。每次詢問一個點集,允許修改一個黑節點為白節點,求點集中代價最大的東西的最小值。
大佬題解:二分答案,然後求超過答案的部分的 LCA。然後將這個點染白,檢查超過答案的部分是不是被卡進答案範圍內了。
這樣做到底對不對呢?首先直覺是,肯定是要將查詢的點中,最長的鏈上的點給染紅。但是到底選擇哪個點呢?因為次大次次大這些點都會對答案有影響。因此我們二分答案,找到剛好的點即可,那麼怎麼判斷呢?對於每個答案,把距離大於這個答案的點選出來,這些點都是對答案有影響的,那麼將這些點的LCA染紅即可,這樣就能一次性降低這些點答案。然後再判斷當前答案是否在二分的答案內即可。如果大的話,證明二分的答案不夠大,小的話就證明答案太小了。
這題毒瘤卡倍增LCA,跟著大佬換成樹鏈剖分就過了,太玄學了。
#include <iostream>
#include <string.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <queue>
using namespace std;
const int MAXN = 100010;
const int INF = 0x3f3f3f3f;
typedef long long ll;
void scan_d(int &ret)
{
char c;
ret = 0;
while ((c = getchar()) < '0' || c > '9');
while (c >= '0' && c <= '9')
{
ret = ret * 10 + (c - '0'), c = getchar();
}
}
struct edge{
int v,w;
int next;
}e[MAXN*2];
int head[MAXN],edge_num;
void insert_edge(int u,int v,int w){
e[edge_num].v=v;
e[edge_num].w=w;
e[edge_num].next=head[u];
head[u]=edge_num++;
}
int N,M,Q;
bool red[MAXN];//紅色的點
int V[MAXN];//查詢的點
int closered[MAXN];//當前點離他最近的紅色的點
ll dis[MAXN];//到根的距離
bool vis[MAXN];
int k;
/****樹鏈剖分求LCA****/
int sz[MAXN];
int son[MAXN];
int top[MAXN];
int dep[MAXN];
int pre[MAXN];
void dfs1(int u, int fa,int w)
{
pre[u]=fa;
sz[u]=1;
if(red[u]){
closered[u]=u;
}
else{
closered[u]=closered[fa];
}
dis[u]=w+dis[fa];
int& maxs = son[u]=-1;
dep[u] = dep[fa] + 1;
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if (v == fa)
continue;
dfs1(v, u, e[i].w);
sz[u]+=sz[v];
if(maxs==-1||sz[v]>sz[maxs])
maxs=v;
}
}
void dfs2(int u,int tp){
top[u]=tp;
if(son[u]!=-1)
dfs2(son[u],tp);
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if (v == pre[u])
continue;
if(v!=son[u])
dfs2(v,v);
}
}
void lca_init()
{
dep[0] = 0;
dfs1(1, 0, 0);
dfs2(1,0);
}
int LCA(int u, int v)
{
int uu = top[u], vv = top[v];
while (uu != vv) {
if (dep[uu] < dep[vv]) {
swap(uu, vv);
swap(u, v);
}
u = pre[uu]; uu = top[u];
}
if (dep[u] < dep[v])
swap(u, v);
return v;
}
/****LCA****/
bool judge(ll ans){
int v;
int lca = -1;
for(int i=0;i<k;i++){
v=V[i];
if(dis[v]-dis[closered[v]]>ans){
vis[v]=1;//標記那些比答案大的點
if(lca==-1)
lca=v;
else{
lca=LCA(lca,v);//求他們的LCA
}
}
else
vis[v]=0;
}
if(lca==-1)
return 1;
//再判斷答案是否在二分的答案內
for(int i=0;i<k;i++){
if(vis[V[i]]){
if(dis[V[i]]-dis[lca]>ans)
return 0;
}
}
return 1;
}
int main()
{
int TT;
scan_d(TT);
while (TT--)
{
edge_num=0;
memset(head,-1,sizeof(head));
memset(red,0,sizeof(red));
scan_d(N);
scan_d(M);
scan_d(Q);
int tmp;
for(int i=0;i<M;i++){
scan_d(tmp);
red[tmp]=1;
}
int u,v,w;
for(int i=0;i<N-1;i++){
scan_d(u);
scan_d(v);
scan_d(w);
insert_edge(u,v,w);
insert_edge(v,u,w);
}
lca_init();
while(Q--){
scan_d(k);
for(int i=0;i<k;i++)
scan_d(V[i]);
ll l=0,r=0;
for(int i=0;i<k;i++)
r=max(r,dis[V[i]]-dis[closered[V[i]]]);
ll m;
while(l<r){
m=(l+r)/2;
if(judge(m)){
r=m;
}
else
l=m+1;
}
printf("%lld\n",l);
}
}
return 0;
}