2018青岛网络赛B

给一颗树,其中树中有一些红色的点,每个点到距离它最近的祖先红点的距离称为它的距离。

每次给一个点子集,可以选择把树中任意一个点变为红色,问怎样让子集里的点的距离最大值最小。

当只有两个点时,肯定是先找到他们的 lca 然后先判断将 lca 染红是否可以让最大的距离变小,如果有一个点的祖先红点在 lca 的子树里那么不管染哪里的点最大的距离最小必定是两个点中的次小值,当有多个点时只需要将点以距离从大到小排序然后依次按这个规则判断即可。

只不过多个点时如果将 lca 染红最大的距离反而比不染变大了,那么染上一个 lca 时的答案时最小的最大距离。

因为给的子集中点总个数不超过1e6,所以复杂度可以接受。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 1e5+7;
int _,n,m,q;
int red[M];
int cnt,head[M],tot,in[M],out[M];
int f[M][22],deep[M],far[M];
ll dis[M];
struct edge
{
    int v,next;ll w;
}e[M<<1];
void init(){
    tot=cnt=0;memset(head,-1,sizeof(head));memset(red,0,sizeof(red));
    memset(dis,0,sizeof(dis));
}
void add(int u,int v,ll w){
    e[++cnt].v=v;e[cnt].w=w;e[cnt].next=head[u];
    head[u]=cnt;
}
void dfs(int u,int fa,int d,ll len,int fared){//dfs(1,-1,0)
    in[u]=++tot;
    deep[u]=d;
    dis[u]=len-dis[fared];
    if(red[u])
        far[u]=u;
    else
        far[u]=fared;
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].v;ll w=e[i].w;
        if(v==fa) continue;
        if(red[u]) dfs(v,u,d+1,dis[u]+w,u);
        else dfs(v,u,d+1,len+w,fared);
        f[v][0]=u;
    }
    out[u]=tot;
    return ;
}
void work(){//RMQ
    for(int i=1;i<20;i++)
        for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1];
}
int lca(int x,int y){//lca
    if(deep[x]<deep[y]) swap(x,y);
    int dt=deep[x]-deep[y];
    for(int i=0;i<20;i++) if(dt&(1<<i)) x=f[x][i];
    if(x==y) return x;
    for(int i=19;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
bool cmp(int &x,int &y){
    return dis[x]>dis[y];
}
int que[M];
int main(){
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);*/
    scanf("%d",&_);
    while(_--){
        init();
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<=m;i++){
            int x;
            scanf("%d",&x);
            red[x]=1;
        }
        for(int i=1;i<n;i++){
            int from,to;ll val;
            scanf("%d%d%lld",&from,&to,&val);
            add(from,to,val);add(to,from,val);
        }
        dfs(1,0,0,0,1);
        work();
        while(q--){
            int k;
            scanf("%d",&k);
            for(int i=1;i<=k;i++){
                scanf("%d",&que[i]);
                if(red[que[i]]) dis[que[i]]=0;
            }
            sort(que+1,que+k+1,cmp);
            ll ans=0;
            int prepos=que[1];
            for(int i=2;i<=k;i++){
                int lc=lca(prepos,que[i]);
                if((in[far[prepos]]>=in[lc]&&in[far[prepos]]<=out[lc])||((in[far[que[i]]]>=in[lc]&&in[far[que[i]]]<=out[lc]))){
                    ans=max(ans,dis[que[i]]);
                    break;
                }
                if(max(ans,dis[que[i]])>=max(dis[que[i]]-dis[lc],ans+dis[prepos]-dis[lc])){
                    ans=max(dis[que[i]]-dis[lc],ans+dis[prepos]-dis[lc]);
                    prepos=lc;
                }
                else{
                    ans=max(ans,dis[que[i]]);
                    break;
                }
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/LMissher/p/9662137.html
今日推荐