树的链接
D国有n个城市,有若干条道路,每条道路能连接两个城市,并且有一定的长度。
初始时,并没有任何道路存在。接下来,有q个操作需要你依次完成:
x y 表示:询问城市x与y之间的最短路径长度;如果不存在任何路径,则你应当回答−1。
x y w 表示:在城市x与y之间修建了一条长度为 w 的道路。保证在此之前在城市 x 与 y 之间不存在任何路径(即:假如在此之前给出一个x y的操作,保证其答案应当为−1)。
输入格式
输入的第一行包含两个正整数n, q。
接下来q行,每行输入两个或者三个正整数,形如x y或x y w,表示一个操作。
每行中,相邻的两个数之间用一个空格隔开。
输出格式
对于每个形如x y的操作,你需要输出一行,包含一个整数,为你对于这次询问的答案。
数据范围
对于测试点1,21,2,保证$ n, q \leq 200$
对于测试点1,2,3,41,2,3,4,保证$ n, q \leq 2000$
对于测试点1,2,3,4,5,61,2,3,4,5,6,保证$ n, q \leq 100,000$
编号为奇数的测试点满足:所有形如 x y w 的询问都出现在所有形如 x y 的询问之后。
对于所有数据,保证$ 1 \leq n, q \leq 500,000$所有的 w 均为不超过1000的正整数。
Lca
数据结构
link(u,v)
query(u,v)
将询问绕(到、跳)着做$\rightarrow$多做几次$\rightarrow$离线
读入,不能用$\color{red}{"cin"}$,因为cin只读第一个可见字符
$\color{blue}{用scanf}$
cin>>n>>q;
for(int i=1;i<=q;i++)
{
cin>>qx[i]>>qy[i];
char c=getchar();
if(c==' ')
{
cin>>qw[i];
add_edge(qx[i],qy[i],qw[i]);
add_edge(qy[i],qx[i],qw[i]);
}
else
qw[i]=-1;
}
for(u=1;u<=n;u++)
if(fa[u][0]==0)
dfs(u);
for(k=1;k<=20;k++)
for(int u=1;u<=n;u++)
{
int v=fa[u][k-1];
fa[u][k]=fa[v][k-1];
g[u][k]=g[v][k-1]+g[u][k-1];
}
for(i=1;i<=q;i++)
{
int u=qx[i];
int v=qy[i];
if(qw[i]==-1)
{
if(find(u)==find[v])
cout<<path(u,v)<<endl;
else
cout<<-1<<endl;
}
else
p[find[u]]=find[v];
}
void dfs(int u)
{
d[u]=d[fa[u][0]]+1;
for(int i=head[u];i;i=e[i].next)
{
v=e[i].to;
if(v!=fa[u][0])
{
g[v][0]=e[i].val;
fa[v][0]=u;
dfs(v);
}
}
}
int path(int u,int v)
{
int sum=0;
if(d[u]<d[v])
return path(v,u);
for(int k=20;k>=0;k--)
{
int uu=fa[u][k];
if(d[uu]>=d[v])
{
sum+=g[u][k];
u=uu;
}
}
if(u==v)
return sum;
for(int k=20;k>=0;k--)
{
int uu=fa[u][k];
int vv=fa[u][k];
if(vv!=uu)
{
sum+=g[u][k];
sum+=g[v][k];
u=uu;
v=vv;
}
}
return sum+g[u][0]+g[v][0];
}