码迷,mamicode.com
首页 > 其他好文 > 详细

2018青岛网络赛B

时间:2018-09-17 15:21:14      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:nbsp   play   技术分享   using   答案   c++   include   排序   display   

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

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

当只有两个点时,肯定是先找到他们的 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

 

2018青岛网络赛B

标签:nbsp   play   技术分享   using   答案   c++   include   排序   display   

原文地址:https://www.cnblogs.com/LMissher/p/9662137.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!