ZOJ - 4048 Red Black Tree(ACM-ICPC 2018 青岛赛区网络预赛 B)(二分+LCA)

版权声明:Why is everything so heavy? https://blog.csdn.net/lzc504603913/article/details/82740568

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, Caizhi
Source: The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online

Submit    Status

题意:给一棵黑白树,定义白节点代价为 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;
}

猜你喜欢

转载自blog.csdn.net/lzc504603913/article/details/82740568