hdu6115 最近公共祖先+树的重心优化

传送门
题意:汉语题目,就不说了。
比赛时不知道每个子公司的办公室时多少,但是我估计这不多,所以大胆的试了一发,结果过了,惊喜中的惊喜。
问两个子公司的最短路,因为是一棵树,所以就是两点之间的路径长度,我们只知道每个子公司的办公室在哪,我是枚举每两个办公室之间的距离,输出最小的那对距离,因为给的树肯定是不规则的,所以我求了一次 树的重心,使得树的高度变低,然后利用最近公共祖先求解距离,因为树高度低了,所以最近公共祖先的时间复杂度也变低了。(就在这时候我发现好像并没有什么优化,最近公共祖先时间复杂度等于两点之间的单位距离,所以我不求树的重心,不优化,交了一遍,发现带重心的优化是6000多ms,不带优化的是8000多ms)
这里写图片描述
这就有点意思了,说明优化一下时间会变快吧。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const double eps = 1e-8;
const LL MOD = 1e9+7;
const int inf=1e9+10;
const int MAXN=100005;
int room[MAXN][100];
int n;
struct node
{
    int to,next,w;
} edge[MAXN*2];
int head[MAXN],tot;
void addedge(int u,int v,int w)
{
    edge[tot].to=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot++;
}
int root,num,min_root;
int vis[MAXN],son[MAXN],max_son[MAXN];
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(room,0,sizeof(room));
}
void dfs_size(int u,int fa)
{
    int v;
    son[u]=1;
    max_son[u]=0;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        v=edge[i].to;
        if(vis[v]||v==fa)
            continue;
        dfs_size(v,u);
        son[u]+=son[v];
        if(max_son[u]<son[v])
            max_son[u]=son[v];
    }
}
void dfs_root(int r,int u,int fa)
{
    int v;
    max_son[u]=max(max_son[u],son[r]-son[u]);
    if(max_son[u]<min_root)
    {
        min_root=max_son[u];
        root=u;
    }
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        v=edge[i].to;
        if(vis[v]||v==fa)
            continue;
        dfs_root(r,v,u);
    }
}
void solve(int u)
{
    min_root=n;
    dfs_size(u,-1);
    dfs_root(u,u,-1);
}
int dep[MAXN],fa_w[MAXN],fa[MAXN];
void get_dep(int u,int d,int pre)
{
    int v;
    dep[u]=d;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        v=edge[i].to;
        if(v==pre)
            continue;
         fa[v]=u;
        fa_w[v]=edge[i].w;
        get_dep(v,d+1,u);
    }
}
int get_dis(int x,int y)
{
    ///根据深度求解,深度深的向上走,最终使得x y深度相同
    int ans=0;
    while(dep[x]>dep[y])
    {
        ans+=fa_w[x];
        x=fa[x];
    }
    while(dep[y]>dep[x])
    {
        ans+=fa_w[y];
        y=fa[y];
    }
    ///当两个点相同时说明找到最近公共祖先了。
    while(x!=y)
    {
        ans+=fa_w[x];
        ans+=fa_w[y];
        x=fa[x];
        y=fa[y];
    }
    return ans;
}
int main()
{
    int m,u,v,w;
    int T;
    int q;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        init();
        for(int i=0; i<n-1; i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        memset(fa_w,0,sizeof(fa_w));
        solve(1);///求数的重心,后来发现不求重心也可以过!!
        fa[root]=root;
        get_dep(root,1,-1);///求每个点的深度,顺便把每个点与其父节
                        ///点之间边的长度记录下来,以及每个节点的父节点记录下来
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&room[i][0]);
             int temp=room[i][0];
            for(int j=1;j<=temp;j++)
                scanf("%d",&room[i][j]);
            sort(room[i]+1,room[i]+1+temp);
            ///手动去重
            room[i][0]=0;
            for(int j=1;j<=temp;j++)
            {
              if(room[i][j]!=room[i][j-1])
              room[i][++room[i][0]]=room[i][j];
            }
        }
        int s,t;
        scanf("%d",&q);
        while(q--)
        {
            int ans=inf;
            scanf("%d%d",&s,&t);
            for(int i=1;i<=room[s][0];i++)
            {
                for(int j=1;j<=room[t][0];j++)
                {
                    int temp=get_dis(room[s][i],room[t][j]);///求两点之间的距离
                    if(temp<ans)
                        ans=temp;
                }
            }
            printf("%d\n",ans);
        }

    }
    return 0;
}

/**

1
3 3
1 2 1
2 3 1
2 1 1
2 2 3

*/
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值