ZOJ4048 Red Black Tree (LCA+二分)

ZOJ Problem Set - 4048

                                        Red Black Tree


Time Limit: 3 Seconds      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.


Author: WENG, Caizhi
Source: The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online

 

 

一、原题地址

点我传送

 

二、大致题意

n个节点,n−1条边,树上的点有m个点是红色的,其余的点是黑色的,题目已知树根1这个点的颜色一定是红色的。

若点为红色,则该点权值为0 ;否则该点的权值为离这个点最近的祖先红点的距离

现在要进行 q 次游戏,每一次的游戏相互独立(即每次游戏后要复原点的情况),每个游戏选出了k 个点,每次可以把一个黑色的点变成红色的点。

询问在改变一个点后使得选出的点的代价最小的情况下,求出权值的最大值。

 

三、大致思路


最小值的最大值自然就想到了二分。

二分答案mid,对于每个mid在检验的时候,将那些大于mid值的点取出来求一个LCA,要使得当前mid成立,那么这个LCA一定是需要被修改的,所以O(n)地查看修改后能否使得权值小于mid 。

数据卡掉了普通的倍增法LCA,需要小小的剪枝。可以采用ST表的LCA或是其他的方法O(1)地进行查询。下面采取的是树剖LCA。
 

四、代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int inf = 0x3f3f3f3f;
#define LL long long int
long long  gcd(long long  a, long long  b) { return a == 0 ? b : gcd(b % a, a); }


int n,m,q;
const int maxn=1e5+5;

struct Edge
{
    int v, next;
    LL w;
}e[maxn<<1];

LL dis[maxn];
int prered[maxn];bool isred[maxn];

int tot,head[maxn],fa[maxn],top[maxn],deep[maxn],siz[maxn],son[maxn],val[maxn];


void addedge(int u, int v, LL w)
{
    e[tot].v = v, e[tot].w = w;
    e[tot].next = head[u];
    head[u] = tot++;
}

void dfs1(int x)
{
    deep[x]=deep[fa[x]]+1;siz[x]=1;
    son[x]=0;
    if(isred[x])prered[x]=x;
    else prered[x]=prered[fa[x]];
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int to=e[i].v;
        if(fa[x]!=to&&!fa[to]){
            val[to]=e[i].w;
            fa[to]=x;
            dis[to]=dis[x]+e[i].w;
            dfs1(to);
            siz[x]+=siz[to];
            if(siz[son[x]]<siz[to])son[x]=to;
        }
    }
}

void dfs2(int x)
{
    if(x==son[fa[x]])top[x]=top[fa[x]];
    else top[x]=x;
    for(int i=head[x];i!=-1;i=e[i].next)if(fa[e[i].v]==x)dfs2(e[i].v);
}

void init(int root)
{
    deep[root]=0;for(int i=1;i<=n;i++)fa[i]=0;
    dfs1(root),dfs2(root);
}

int query(int x,int y)//返回lca
{
    for(;top[x]!=top[y];deep[top[x]]>deep[top[y]]?x=fa[top[x]]:y=fa[top[y]]);
    return deep[x]<deep[y]?x:y;
}



void read()
{
    scanf("%d %d %d",&n,&m,&q);
    for(int i=1;i<=n;i++)isred[i]=false,head[i]=-1,dis[i]=0;

    for(int i=1;i<=m;i++)
    {
        int x;scanf("%d",&x);
        isred[x]=true;
    }
    tot=0;
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        LL w;
        scanf("%d %d %lld",&u,&v,&w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
}

int Q[maxn],Qtop;
LL maxx;
vector<int>ck;

bool check(LL mid)
{
    ck.clear();
    int lca=-1;
    for(int i=1;i<=Qtop;i++)
    {
        if(dis[Q[i]]-dis[prered[Q[i]]]>mid)
        {
            if(lca==-1)lca=Q[i];
            else
            {
                lca=query(lca,Q[i]);
            }
            ck.push_back(Q[i]);
        }
    }
    if(lca==-1)return true;
    int Si=ck.size();
    for(int i=0;i<Si;i++)
    {
        if(dis[ck[i]]-dis[lca]>mid)return false;
    }
    return true;
}

LL bir_ser()
{
    LL l=0,r=maxx;
    LL mid=maxx,ans;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(check(mid))
        {
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    return ans;

}

void work()
{
    for(int i=1;i<=q;i++)
    {
        int num;
        scanf("%d",&num);
        maxx=Qtop=0;
        while(num--)
        {
            int x;
            scanf("%d",&x);Q[++Qtop]=x;
            LL nowdis=dis[x]-dis[prered[x]];
            maxx=max(maxx,nowdis);
        }
        printf("%lld\n",bir_ser());
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        read();
        init(1);
        work();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值