考虑如何寻找并记录两点之间的距离。发现
n
≤
2
×
1
0
5
n \le 2\times10^5
n≤2×105,故两点之间距离可以用 map 存。对于子树上的点到其上环点的距离,暴力搜索即可。对于环上两点间的距离,可以用相对的思想,即用
d
i
s
u
dis_u
disu 表示环上一点
u
u
u 到环上另一固定点
s
t
a
r
t
p
o
i
n
t
startpoint
startpoint 的距离,那么环上两点
u
u
u、
v
v
v 间的距离便是
min
(
∣
d
i
s
u
−
d
i
s
v
∣
,
t
o
t
−
∣
d
i
s
u
−
d
i
s
v
∣
)
\min (|dis_u-dis_v|,tot-|dis_u-dis_v|)
min(∣disu−disv∣,tot−∣disu−disv∣),
t
o
t
tot
tot 为环上的点的总个数。
时间复杂度为
O
(
(
n
+
q
)
log
n
)
\mathcal O((n+q)\log n)
O((n+q)logn)。
代码
1
1
1
#include<bits/stdc++.h>usingnamespace std;constint maxn =2e5+100;int n, q;int head[maxn], cnt, cnti, cntii, headii[maxn];structedgei{int u, v;} ei[maxn <<1];structedge{int v, next;} e[maxn <<1], eii[maxn <<1];voidadd(int tp,int u,int v){if(tp ==1)
ei[++ cnti]=(edgei){u, v};elseif(tp ==0)
e[++ cnt]=(edge){v, head[u]},
head[u]= cnt;else{
eii[++ cntii]=(edge){v, headii[u]},
headii[u]= cntii;}}
map <pair <int,int>,int> dis;voidaddi(int x,int y,int w){
dis.insert(make_pair(make_pair(x, y), w));}intfquery(int x,int y){return(dis.find(make_pair(x, y))-> second);}int f[maxn], vis[maxn], depth[maxn], fi[maxn];intfind(int x){while(x != f[x]) x = f[x]= f[f[x]];return x;}voiddfs(int u,int fa,int d){
depth[u]= d, fi[u]= fa;for(int i = head[u]; i; i = e[i].next){int v = e[i].v;if(v == fa)continue;dfs(v, u, d +1);}}int zl[maxn], zp, rl[maxn], rp;voidfind_circle(int x,int y){if(depth[x]< depth[y])swap(x, y);while(depth[x]> depth[y]){
zl[++ zp]= x;
x = fi[x];}if(x == y){
zl[++ zp]= x;return;}while(fi[x]!= fi[y]){
zl[++ zp]= x, rl[++ rp]= y;
x = fi[x], y = fi[y];}
zl[++ zp]= x, rl[++ rp]= y;
zl[++ zp]= fi[x];return;}int wl[maxn], visi[maxn], p;voidKruskal(){for(int i =1; i <= n; i++)
f[i]= i;for(int i =1; i <= n; i++){int fx =find(ei[i].u),
fy =find(ei[i].v);if(fx == fy)continue;
f[fy]= fx, vis[i]=1;add(0, ei[i].u, ei[i].v);add(0, ei[i].v, ei[i].u);}dfs(1,0,1);for(int i =1; i <= n; i++){if(!vis[i]){find_circle(ei[i].u, ei[i].v);for(int j =1; j <= zp; j++){
wl[++ p]= zl[j];
visi[zl[j]]=1;addi(wl[p], wl[1], p -1);}for(int j = rp; j >=1; j--){
wl[++ p]= rl[j];
visi[rl[j]]=1;addi(wl[p], wl[1], p -1);}break;}}}int fii[maxn];voiddfsi(int u,int fa,int fai,int d){
fii[u]= fai;addi(u, fai, d);for(int i = headii[u]; i; i = eii[i].next){int v = eii[i].v;if(v == fa || visi[v])continue;dfsi(v, u, fai, d +1);}}intmain(){scanf("%d %d",&n,&q);for(int i =1; i <= n; i++){int u, v;scanf("%d %d",&u,&v);add(1, u, v);add(2, u, v),add(2, v, u);}Kruskal();for(int i =1; i <= n; i++)if(visi[i])dfsi(i, i, i,0);while(q --){int a, b;scanf("%d %d",&a,&b);int di =fquery(b, fii[b]),
dii =fquery(a, fii[a]),
diii =fquery(fii[a], wl[1]),
div =fquery(fii[b], wl[1]);int dv =min(abs(diii - div), p -abs(diii - div));if(di + dv > dii)printf("Survive\n");elseprintf("Deception\n");}return0;}
思路
2
2
2
O
(
n
)
\mathcal O(n)
O(n) 正解
赛后知道了这种图叫做基环树(
n
n
n 个点,
n
n
n 条边的联通图),可以实现
O
(
n
)
\mathcal O(n)
O(n) 找环。
发现不用 map 便可以存储距离。我们可以用
d
i
s
i
dis_i
disi 表示某一子树上的
i
i
i 到其上环点的距离,用
f
i
i
fi_i
fii 表示
i
i
i 的上环点,用
d
i
s
i
i
disi_i
disii 表示环上的一点
i
i
i 到环上另一固定点
s
t
a
r
t
p
o
i
n
t
startpoint
startpoint 的距离,便实现了
O
(
1
)
\mathcal O(1)
O(1) 查询。
时间复杂度为
O
(
n
+
q
)
\mathcal O(n+q)
O(n+q)。
代码
2
2
2
#include<bits/stdc++.h>usingnamespace std;constint maxn =2e5+100;int head[maxn], cnt;structedge{int v, next;} e[maxn <<1];voidadd(int u,int v){
e[++ cnt]=(edge){v, head[u]};
head[u]= cnt;}int n, q;int dfn[maxn], vis[maxn], p, f[maxn], tot;// 找环。voidgetloop(int u,int fa){
dfn[u]=++ p;for(int i = head[u]; i; i = e[i].next){int v = e[i].v;if(v == fa)continue;if(dfn[v]){if(dfn[v]< dfn[u])continue;for(int x = v; x != f[u]; x = f[x])
vis[x]=1, tot ++;}else f[v]= u,getloop(v, u);}}// 得环上一点到固定点的距离。int dis[maxn], start_point, disi[maxn];voiddfs(int u,int fa,int d){
disi[u]= d;for(int i = head[u]; i; i = e[i].next){int v = e[i].v;if(v == fa ||!vis[v]|| v == start_point)continue;dfs(v, u, d +1);if(u == start_point)return;// 避免在把环倒着搜一遍。}}// 找子树上的点的上环点和到上换点的距离。int fi[maxn];voiddfsi(int u,int fa,int fai,int d){
fi[u]= fai, dis[u]= d;for(int i = head[u]; i; i = e[i].next){int v = e[i].v;if(v == fa || vis[v])continue;dfsi(v, u, fai, d +1);}}intmain(){scanf("%d %d",&n,&q);for(int i =1; i <= n; i++){int u, v;scanf("%d %d",&u,&v);add(u, v),add(v, u);}getloop(1,0);for(int i =1; i <= n; i++){if(!vis[i])continue;if(!start_point)start_point = i,dfs(i, f[i],0);dfsi(i, i, i,0);}while(q --){int a, b;scanf("%d %d",&a,&b);int di, dii, diii;
di = dis[b], dii = dis[a];// 相对得环上两点之间得距离。
diii =abs(disi[fi[a]]- disi[fi[b]]);
diii =min(diii, tot - diii);// 赶不到则是 A 胜利。if(di + diii > dii)printf("Survive\n");elseprintf("Deception\n");}return0;}