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