题目大意
在山谷中有N个村庄,有N-1条道路将村庄连成一棵树,每条道路有一定长度。第E号村庄为山谷的出口。有S个村庄中有商店,商店可提供补给。
由于山谷中天气恶劣,一条道路会被封死。当你处于某一个村庄中时,得知一条道路已经封死,你想知道自己能否活下去,即对于每一个询问,你需要计算,你是否能走出山谷,如果不能走出,则计算到最近的商店获得补给品所需最短路程。
输入
第一行:N,S,Q,E,即 村庄数,商店数,询问数,出口编号
以下N-1行:A,B,W,表示一条连接村庄A,B的长度为W的道路
以下S行:C,表示商店所在村庄编号
一下Q行:I,R,表示询问:第I号道路封死,你在R号村庄的结果
输出
对每个询问,如果能逃出山谷,输出"escaped"
如果不能,输出最近商店距离
如果不能走到商店,输出"oo"
题解
以出口E为整颗树的根。
先计算每个结点的dfs序编号,可以通过dfs序可知R村庄是否在I号边的子树中,很容易判断是否输出escaped
接下来计算最近商店距离
考虑dp
d
p
[
u
]
dp[u]
dp[u]表示结点u的子树中离结点u最近的商店距离。
d
e
p
[
u
]
dep[u]
dep[u]表示根到结点u的距离。
则,若
v
∈
s
o
n
[
u
]
v\isin son[u]
v∈son[u],
d
p
[
u
]
=
m
i
n
(
d
p
[
v
]
+
l
e
n
[
u
→
v
]
)
dp[u]=min(dp[v]+len[u\rightarrow v])
dp[u]=min(dp[v]+len[u→v])
距结点u最近商店的距离:
设
v
v
v在
u
u
u到边
I
I
I路径上的点
a
n
s
[
u
]
=
m
i
n
(
d
p
[
v
]
+
d
e
p
[
u
]
−
d
e
p
[
v
]
)
=
m
i
n
(
d
p
[
v
]
−
d
e
p
[
v
]
)
+
d
e
p
[
u
]
ans[u]=min(dp[v]+dep[u]-dep[v])=min(dp[v]-dep[v])+dep[u]
ans[u]=min(dp[v]+dep[u]−dep[v])=min(dp[v]−dep[v])+dep[u]
由此受到启发,可以设
v
a
l
[
u
]
=
d
p
[
u
]
−
d
e
p
[
u
]
val[u]=dp[u]-dep[u]
val[u]=dp[u]−dep[u]
则
a
n
s
[
u
]
=
d
e
p
[
u
]
−
m
a
x
(
v
a
l
[
v
]
)
ans[u]=dep[u]-max(val[v])
ans[u]=dep[u]−max(val[v])
即问题转换为求
u
u
u到边
I
I
I上结点的
v
a
l
val
val最大值,且
v
a
l
val
val可以预处理。
可以用倍增实现。
代码
我写的丑,考试时居然用树链剖分来做。。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=100005,MAXLOG=18;
const long long LLF=0x3F3F3F3F3F3F3F3FLL;
int N,S,Q,E;
vector<pair<int,int> > adj[MAXN];
int dfc;
int fa[MAXN],len[MAXN],siz[MAXN],st[MAXN],ed[MAXN],nd[MAXN],son[MAXN],tp[MAXN];
long long dep[MAXN],dp[MAXN];
bool shop[MAXN];
int edges[MAXN][3];
void dfs1(int u)
{
siz[u]=1;
for(int i=0;i<(int)adj[u].size();i++)
{
int v=adj[u][i].first;
if(v!=fa[u])
{
fa[v]=u;
len[v]=adj[u][i].second;
dep[v]=dep[u]+len[v];
dfs1(v);
siz[u]+=siz[v];
}
}
}
void dfs2(int u,int t)
{
st[u]=++dfc;
nd[dfc]=u;
tp[u]=t;
for(int i=0;i<(int)adj[u].size();i++)
{
int v=adj[u][i].first;
if(v!=fa[u]&&siz[v]>siz[son[u]])
son[u]=v;
}
if(son[u])
dfs2(son[u],t);
for(int i=0;i<(int)adj[u].size();i++)
{
int v=adj[u][i].first;
if(v!=fa[u]&&v!=son[u])
dfs2(v,v);
}
ed[u]=dfc;
}
void DP(int u)
{
dp[u]=dep[u];
long long tmp=shop[u]?0:LLF;
for(int i=0;i<(int)adj[u].size();i++)
{
int v=adj[u][i].first;
if(v!=fa[u])
{
DP(v);
tmp=min(tmp,-(dp[v]-dep[v])+adj[u][i].second);
}
}
dp[u]-=tmp;
}
long long mx[MAXN][MAXLOG],lg[MAXN];
void InitST()
{
for(int i=1;i<=N;i++)
mx[i][0]=dp[nd[i]];
for(int j=1;(1<<j)<=N;j++)
for(int i=1;i+(1<<j)-1<=N;i++)
mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
for(int i=2;i<=N;i++)
lg[i]=lg[i/2]+1;
}
long long GetMax(int l,int r)
{
int t=lg[r-l+1];
return max(mx[l][t],mx[r-(1<<t)+1][t]);
}
long long solve(int u,int v)
{
long long ret=dep[u],tmp=-LLF;
while(dep[tp[u]]>=dep[v])
{
tmp=max(tmp,GetMax(st[tp[u]],st[u]));
u=fa[tp[u]];
}
if(dep[u]>=dep[v])
tmp=max(tmp,GetMax(st[v],st[u]));
ret-=tmp;
return ret;
}
int main()
{
freopen("valley.in","r",stdin);
freopen("valley.out","w",stdout);
scanf("%d%d%d%d",&N,&S,&Q,&E);
for(int i=1;i<N;i++)
{
scanf("%d%d%d",&edges[i][0],&edges[i][1],&edges[i][2]);
adj[edges[i][0]].push_back(make_pair(edges[i][1],edges[i][2]));
adj[edges[i][1]].push_back(make_pair(edges[i][0],edges[i][2]));
}
for(int i=1,u;i<=S;i++)
{
scanf("%d",&u);
shop[u]=true;
}
dfs1(E);
dfs2(E,E);
for(int i=1;i<N;i++)
if(fa[edges[i][1]]==edges[i][0])
swap(edges[i][0],edges[i][1]);
DP(E);
InitST();
while(Q--)
{
int I,R;
scanf("%d%d",&I,&R);
if(st[edges[I][0]]<=st[R]&&st[R]<=ed[edges[I][0]])
{
long long ans=solve(R,edges[I][0]);
if(ans>=LLF)
puts("oo");
else
printf("%lld\n",ans);
}
else
puts("escaped");
}
fclose(stdin);
fclose(stdout);
return 0;
}