2.15 LCA

Nearest Common Ancestors POJ 1330

题意:给出一棵树, 询问两个点的最近公共祖先。

思路:

$LCA$模板题,请各位掏出各式各样的模板A穿它。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue> using namespace std; typedef long long ll;
const int maxn = 1e4 + ;
const int DEG = ; struct Edge {
int to, nxt;
Edge() {}
Edge(int to, int nxt) :to(to), nxt(nxt) {}
}edge[maxn << ]; int head[maxn], tot; void Init()
{
tot = ;
memset(head, -, sizeof head);
} void addedge(int u, int v)
{
edge[tot] = Edge(v, head[u]); head[u] = tot++;
edge[tot] = Edge(u, head[v]); head[v] = tot++;
} int fa[maxn][DEG];
int deg[maxn]; void BFS(int root)
{
queue<int>q;
deg[root] = ;
fa[root][] = root;
q.push(root);
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int i = ; i < DEG; ++i)
{
fa[tmp][i] = fa[fa[tmp][i - ]][i - ];
}
for (int i = head[tmp]; i != -; i = edge[i].nxt)
{
int v = edge[i].to;
if (v == fa[tmp][]) continue;
deg[v] = deg[tmp] + ;
fa[v][] = tmp;
q.push(v);
}
}
} int LCA(int u, int v)
{
if (deg[u] > deg[v]) swap(u, v);
int hu = deg[u];
int hv = deg[v];
int tu = u, tv = v;
for (int det = hv - hu, i = ; det; det >>= , ++i)
{
if (det & )
{
tv = fa[tv][i];
}
}
if (tu == tv) return tu;
for (int i = DEG - ; i >= ; --i)
{
if (fa[tu][i] == fa[tv][i])
continue;
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][];
} int n;
bool flag[maxn]; void RUN()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
Init();
memset(flag, false, sizeof flag);
for (int i = ; i < n; ++i)
{
int u, v;
ll w;
scanf("%d %d", &u, &v);
addedge(u, v);
flag[v] = true;
}
int root = ;
for (int i = ; i <= n; ++i)
{
if (!flag[i])
{
root = i;
break;
}
}
BFS(root);
int u, v;
scanf("%d %d", &u, &v);
printf("%d\n", LCA(u, v));
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGE
return ;
}

Closest Common Ancestors POJ 1470

题意:给出一棵树,然后给出几组询问,记录每个点作为询问中的最近公共祖先的次数并输出,如果次数为0则不输出。

思路:

$LCA$模板题,建图稍微注意一下后对于每次询问都记录他的最近最近公共祖先,输出。

 #include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue> using namespace std; typedef long long ll;
const int maxn = 1e5 + ;
const int DEG = ; struct Edge {
int to, nxt;
Edge() {}
Edge(int to, int nxt) :to(to), nxt(nxt) {}
}edge[maxn << ]; int head[maxn], tot; int dis[maxn]; void addedge(int u, int v)
{
edge[tot] = Edge(v, head[u]);
head[u] = tot++;
} void Init()
{
tot = ;
memset(head, -, sizeof head);
} int fa[maxn][DEG];
int deg[maxn]; void BFS(int root)
{
queue<int>q;
deg[root] = ;
fa[root][] = root;
q.push(root);
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int i = ; i < DEG; ++i)
{
fa[tmp][i] = fa[fa[tmp][i - ]][i - ];
}
for (int i = head[tmp]; i != -; i = edge[i].nxt)
{
int v = edge[i].to;
if (v == fa[tmp][]) continue;
deg[v] = deg[tmp] + ;
fa[v][] = tmp;
q.push(v);
}
}
} int LCA(int u, int v)
{
if (deg[u] > deg[v]) swap(u, v);
int hu = deg[u];
int hv = deg[v];
int tu = u, tv = v;
for (int det = hv - hu, i = ; det; det >>= , ++i)
{
if (det & )
{
tv = fa[tv][i];
}
}
if (tu == tv) return tu;
for (int i = DEG - ; i >= ; --i)
{
if (fa[tu][i] == fa[tv][i])
continue;
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][];
} int n, m;
char str[maxn];
bool flag[maxn];
int arr[maxn]; void RUN()
{
while (~scanf("%d", &n))
{
Init();
memset(flag, false, sizeof flag);
memset(arr, , sizeof arr);
for (int i = ; i <= n; ++i)
{
int id, m;
scanf("%d:(%d)", &id, &m);
for (int i = ; i < m; ++i)
{
int id2;
scanf("%d", &id2);
addedge(id, id2);
flag[id2] = true;
}
}
int root = ;
for (int i = ; i <= n; ++i)
{
if (!flag[i])
{
root = i;
break;
}
}
BFS(root);
int q;
scanf("%d", &q);
while (q--)
{
int u, v;
scanf(" (%d %d)", &u, &v);
int root = LCA(u, v);
arr[root]++;
}
for (int i = ; i <= n; ++i)
{
if (arr[i])
{
printf("%d:%d\n", i, arr[i]);
}
}
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGE
return ;
}

How far away ?HDU 2586

题意:给出一棵树,每条边有边权,然后给出几组询问,每组询问需要输出$u, v$之间的最短路径。

思路:

对于一棵树,两个点的路径唯一。

这两个点的路径长度为u到根的路径长度加上v到根的路径长度减去两倍LCA(u, v)到根的路径长度。

即$dis_u+dis_v-2 \cdot dis_{LCA_{u, v}}$。

 #include<bits/stdc++.h>

 using namespace std;

 typedef long long ll;

 const int maxn = 1e5 + ;
const int DEG = ; int dis[maxn]; struct Edge {
int to;
int next;
int w;
Edge() {}
Edge(int to, int next, int w) :to(to), next(next), w(w) {}
}edge[maxn << ]; int head[maxn];
int tot; void addedge(int u, int v, int w)
{
edge[tot] = Edge(v, head[u], w);
head[u] = tot++;
} void init()
{
tot = ;
memset(head, -, sizeof head);
memset(dis, 0x3f, sizeof dis);
} int fa[maxn][DEG];
int deg[maxn]; void BFS(int root)
{
queue<int>q;
deg[root] = ;
fa[root][] = root;
q.push(root);
dis[root] = ;
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int i = ; i < DEG; ++i)
{
fa[tmp][i] = fa[fa[tmp][i - ]][i - ];
}
for (int i = head[tmp]; i != -; i = edge[i].next)
{
int v = edge[i].to;
if (v == fa[tmp][]) continue;
dis[v] = dis[tmp] + edge[i].w;
deg[v] = deg[tmp] + ;
fa[v][] = tmp;
q.push(v);
}
}
} int LCA(int u, int v)
{
if (deg[u] > deg[v])
swap(u, v);
int hu = deg[u], hv = deg[v];
int tu = u;
int tv = v;
for (int det = hv - hu, i = ; det; det >>= , ++i)
{
if (det & )
{
tv = fa[tv][i];
}
}
if (tu == tv) return tu;
for (int i = DEG - ; i >= ; --i)
{
if (fa[tu][i] == fa[tv][i])
continue;
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][];
} void RUN()
{
int t;
scanf("%d", &t);
while (t--)
{
init();
int n, q;
scanf("%d %d", &n, &q);
for (int i = ; i < n; ++i)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
addedge(u, v, w);
addedge(v, u, w);
}
BFS();
while (q--)
{
int u, v;
scanf("%d %d", &u, &v);
int root = LCA(u, v);
int ans = dis[u] + dis[v] - * dis[root];
printf("%d\n", ans);
}
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGE
return ;
}

Connections between cities HDU 2874

题意:给出一张图,保证无环,但是不保证连通,求出两个点的最短距离,如果不连通就输出$"Not connected"$。

思路:

由于保证无环,那么给出的图就是一颗或者多颗树。

那么可以用并查集来维护两点是否在一个集合中,也就是一棵树中。

对于在一棵树上的$u,v$两点,距离就是$dis_u+dis_v-2 \cdot dis_{LCA_{u, v}}$。

 #include<bits/stdc++.h>

 using namespace std;

 typedef long long ll;

 const int maxn = 1e5 + ;
const int DEG = ; int n, m, q;
int Fa[maxn]; int dis[maxn]; int find(int x)
{
return x == Fa[x] ? Fa[x] : Fa[x] = find(Fa[x]);
} void mix(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)
{
Fa[x] = y;
}
} struct Edge {
int to;
int next;
int w;
Edge() {}
Edge(int to, int next, int w) :to(to), next(next), w(w) {}
}edge[maxn << ]; int head[maxn];
int tot; void addedge(int u, int v, int w)
{
edge[tot] = Edge(v, head[u], w);
head[u] = tot++;
} void init()
{
tot = ;
memset(head, -, sizeof head);
memset(dis, , sizeof dis);
for (int i = ; i <= n; ++i)
{
Fa[i] = i;
}
} int fa[maxn][DEG];
int deg[maxn]; void BFS()
{
queue<int>q;
for (int i = ; i <= n; ++i)
{
if (find(i) == i)
{
deg[i] = ;
fa[i][] = i;
dis[i] = ;
q.push(i);
}
}
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int i = ; i < DEG; ++i)
{
fa[tmp][i] = fa[fa[tmp][i - ]][i - ];
}
for (int i = head[tmp]; i != -; i = edge[i].next)
{
int v = edge[i].to;
if (v == fa[tmp][]) continue;
dis[v] = dis[tmp] + edge[i].w;
deg[v] = deg[tmp] + ;
fa[v][] = tmp;
q.push(v);
}
}
} int LCA(int u, int v)
{
if (deg[u] > deg[v])
swap(u, v);
int hu = deg[u], hv = deg[v];
int tu = u;
int tv = v;
for (int det = hv - hu, i = ; det; det >>= , ++i)
{
if (det & )
{
tv = fa[tv][i];
}
}
if (tu == tv) return tu;
for (int i = DEG - ; i >= ; --i)
{
if (fa[tu][i] == fa[tv][i])
continue;
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][];
} void solve()
{
int u, v;
scanf("%d %d", &u, &v);
int rootu = find(u);
int rootv = find(v);
if (rootu != rootv)
{
puts("Not connected");
return;
}
int root = LCA(u, v);
int ans = dis[u] + dis[v] - * dis[root];
printf("%d\n", ans);
} void RUN()
{
while (~scanf("%d %d %d", &n, &m, &q))
{
init();
for (int i = ; i < m; ++i)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
addedge(u, v, w);
addedge(v, u, w);
mix(u, v);
}
BFS();
while (q--)
{
solve();
}
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGE
return ;
}

Network POJ - 3417

题意:给出一棵树和一些非树边,你可以选择破坏一条树边以及一条非树边,问有多少种方式可以破坏这棵树。

思路:

对于每条非树边$u-v$都可以形成一个$u-LCA_{u, v}-v-u$的环,如果破坏这个这个环则只能选择环上一个树边以及新加入的非树边。

如果某条树边被多个环覆盖那么无法通过破坏这条边以及非树边来破坏整棵树。

如果某条树边没有被环覆盖那么可以通过破坏这条树边以及任意一条非树边来破坏整棵树。

最后可以通过$LCA$以及树上差分(前缀和?)来优化求出每条树边被多少个环覆盖。

 #include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue> using namespace std; typedef long long ll;
const int maxn = 1e6 + ;
const int DEG = ; struct Edge {
int to, nxt;
Edge() {}
Edge(int to, int nxt) :to(to), nxt(nxt) {}
}edge[maxn << ]; int head[maxn], tot; int dis[maxn]; void addedge(int u, int v)
{
edge[tot] = Edge(v, head[u]);
head[u] = tot++;
} void init()
{
memset(head, -, sizeof head);
tot = ;
} int fa[maxn][DEG];
int deg[maxn]; void BFS(int root)
{
queue<int>q;
deg[root] = ;
fa[root][] = root;
q.push(root);
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int i = ; i < DEG; ++i)
{
fa[tmp][i] = fa[fa[tmp][i - ]][i - ];
}
for (int i = head[tmp]; i != -; i = edge[i].nxt)
{
int v = edge[i].to;
if (v == fa[tmp][]) continue;
deg[v] = deg[tmp] + ;
fa[v][] = tmp;
q.push(v);
}
}
} int LCA(int u, int v)
{
if (deg[u] > deg[v]) swap(u, v);
int hu = deg[u];
int hv = deg[v];
int tu = u, tv = v;
for (int det = hv - hu, i = ; det; det >>= , ++i)
{
if (det & )
{
tv = fa[tv][i];
}
}
if (tu == tv) return tu;
for (int i = DEG - ; i >= ; --i)
{
if (fa[tu][i] == fa[tv][i])
continue;
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][];
} int n, m;
ll arr[maxn]; void DFS(int u, int pre)
{
for (int i = head[u]; ~i; i = edge[i].nxt)
{
int v = edge[i].to;
if (v == pre) continue;
DFS(v, u);
arr[u] += arr[v];
}
} void RUN()
{
while (~scanf("%d %d", &n, &m))
{
init();
memset(arr, , sizeof arr);
for (int i = ; i < n; ++i)
{
int u, v;
scanf("%d %d", &u, &v);
addedge(u, v);
addedge(v, u);
}
BFS();
for (int i = ; i <= m; ++i)
{
int u, v;
scanf("%d %d", &u, &v);
arr[u]++;
arr[v]++;
arr[LCA(u, v)] -= ;
}
DFS(, -);
ll ans = ;
for (int i = ; i <= n; ++i)
{
if (!arr[i])
{
ans += m;
}
else if (arr[i] == )
{
ans++;
}
}
printf("%lld\n", ans);
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGE return ;
}

Imperial roads UVALive - 8196

题意:给出一张图,然后询问给出一条边,求有这条边的最小生成树的权值和。

思路:

先求最小生成树,然后询问的边如果在最小生成树里面那么就是原来的最小生成树的权值和。

否则在原来的最小生成树里面的加入一条边,形成个环,然后去掉这个环里面除了加入的边之外的边权最大的边即可。

 #include<bits/stdc++.h>

 using namespace std;

 typedef long long ll;

 const int maxn = 2e5 + ;
const int DEG = ; int n, m; struct node {
int u, v;
ll w;
node() {}
node(int u, int v, ll w) :u(u), v(v), w(w) {}
inline bool operator < (const node &b)const
{
return w < b.w;
}
}; struct Edge {
int to;
int nxt;
ll w;
Edge() {}
Edge(int to, int nxt, ll w) :to(to), nxt(nxt), w(w) {}
}edge[maxn << ]; int dis[maxn][DEG];
bool inMST[maxn << ];
int head[maxn];
int tot;
int father[maxn];
vector<node>G;
int cnt;
map<pair<int, int>, int>mp; void addedge(int u, int v, ll w)
{
edge[tot] = Edge(v, head[u], w); head[u] = tot++;
edge[tot] = Edge(u, head[v], w); head[v] = tot++;
} void Init()
{
G.clear();
cnt = ;
tot = ;
memset(dis, , sizeof dis);
memset(head, -, sizeof head);
for (int i = ; i <= n; ++i)
{
father[i] = i;
}
} int find(int x)
{
return x == father[x] ? father[x] : father[x] = find(father[x]);
} void mix(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)
{
father[x] = y;
}
} bool same(int x, int y)
{
return find(x) == find(y);
} ll MST()
{
mp.clear();
ll res = ;
sort(G.begin(), G.end());
memset(inMST, false, sizeof inMST);
for (auto it : G)
{
int u = it.u;
int v = it.v;
mp[make_pair(v, u)] = cnt;
mp[make_pair(u, v)] = cnt++;
}
for (auto it : G)
{
int u = it.u;
int v = it.v;
if (same(u, v)) continue;
mix(u, v);
inMST[mp[make_pair(u, v)]] = true;
addedge(u, v, it.w);
res += it.w;
}
return res;
} int fa[maxn][DEG];
int deg[maxn]; void BFS(int root)
{
queue<int>q;
deg[root] = ;
fa[root][] = root;
q.push(root);
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int i = ; i < DEG; ++i)
{
dis[tmp][i] = max(dis[tmp][i - ], dis[fa[tmp][i - ]][i - ]);
fa[tmp][i] = fa[fa[tmp][i - ]][i - ];
}
for (int i = head[tmp]; ~i; i = edge[i].nxt)
{
int v = edge[i].to;
if (v == fa[tmp][]) continue;
deg[v] = deg[tmp] + ;
fa[v][] = tmp;
int id = mp[make_pair(tmp, v)];
dis[v][] = G[id].w;
q.push(v);
}
}
} int LCA(int u, int v)
{
int res = ;
if (deg[u] > deg[v])
swap(u, v);
int hu = deg[u], hv = deg[v];
int tu = u, tv = v;
for (int det = hv - hu, i = ; det; det >>= , ++i)
{
if (det & )
{
res = max(res, dis[tv][i]);
tv = fa[tv][i];
}
}
if (tu == tv) return res;
for (int i = DEG - ; i >= ; --i)
{
if (fa[tu][i] == fa[tv][i]) continue;
res = max(res, max(dis[tu][i], dis[tv][i]));
tu = fa[tu][i];
tv = fa[tv][i];
}
res = max(res, max(dis[tu][], dis[tv][]));
return res;
} void RUN()
{
while (~scanf("%d %d", &n, &m))
{
Init();
for (int i = ; i <= m; ++i)
{
int u, v;
ll w;
scanf("%d %d %lld", &u, &v, &w);
G.push_back(node(u, v, w));
}
ll ans = MST();
BFS();
int q;
scanf("%d", &q);
while (q--)
{
int u, v;
scanf("%d %d", &u, &v);
int id = mp[make_pair(u, v)];
ll w = G[id].w;
if (inMST[id])
{
printf("%lld\n", ans);
}
else
{
ll res = LCA(u, v);
printf("%lld\n", ans + w - res);
}
}
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGE return ;
}

CD操作 HDU - 4547

题意:

思路:

从$u-v$的操作数为$dis_u-dis_{LCA{u, v}}$

 #include<bits/stdc++.h>

 using namespace std;

 typedef long long ll;
const int maxn = 1e5 + ;
const int DEG = ; struct Edge {
int to, nxt;
inline Edge() {}
inline Edge(int to, int nxt) :to(to), nxt(nxt) {}
}edge[maxn << ]; int head[maxn], tot; map<string, int>mp;
int dis[maxn]; inline void addedge(int u, int v)
{
edge[tot] = Edge(v, head[u]);
head[u] = tot++;
} inline void init()
{
memset(head, -, sizeof head);
tot = ;
} int fa[maxn][DEG];
int deg[maxn]; inline void BFS(int root)
{
queue<int>q;
deg[root] = ;
dis[root] = ;
fa[root][] = root;
q.push(root);
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int i = ; i < DEG; ++i)
{
fa[tmp][i] = fa[fa[tmp][i - ]][i - ];
}
for (int i = head[tmp]; i != -; i = edge[i].nxt)
{
int v = edge[i].to;
if (v == fa[tmp][]) continue;
deg[v] = deg[tmp] + ;
dis[v] = dis[tmp] + ;
fa[v][] = tmp;
q.push(v);
}
}
} inline int LCA(int u, int v)
{
if (deg[u] > deg[v]) swap(u, v);
int hu = deg[u];
int hv = deg[v];
int tu = u, tv = v;
for (int det = hv - hu, i = ; det; det >>= , ++i)
{
if (det & )
{
tv = fa[tv][i];
}
}
if (tu == tv) return tu;
for (int i = DEG - ; i >= ; --i)
{
if (fa[tu][i] == fa[tv][i])
continue;
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][];
} int n, m;
bool flag[maxn]; inline void RUN()
{
int t;
cin >> t;
while (t--)
{
cin >> n >> m;
init();
mp.clear();
int cnt = ;
memset(flag, false, sizeof flag);
for (int i = ; i < n; ++i)
{
string s1, s2;
cin >> s1 >> s2;
if (mp[s1] == )
{
mp[s1] = cnt++;
}
if (mp[s2] == )
{
mp[s2] = cnt++;
}
addedge(mp[s2], mp[s1]);
addedge(mp[s1], mp[s2]);
flag[mp[s1]] = true;
}
int root = ;
for (int i = ; i < cnt; ++i)
{
if (!flag[i])
{
root = i;
break;
}
}
BFS(root);
while (m--)
{
string s1, s2;
cin >> s1 >> s2;
int u = mp[s1];
int v = mp[s2];
int tmp = LCA(u, v);
int ans = dis[u] - dis[tmp];
if (tmp != v)
{
ans++;
}
cout << ans << endl;
}
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGE return ;
}

Red Black Tree ZOJ - 4048

题意:有一个树,上面有红点和黑点,有边权,每个点的花费定义为它到离它最近的红点的距离,每次询问给出一些点,能够将这棵树中的一个黑点变为红点,使得这些点中的最大花费最小

思路:

二分答案,符合条件的点不管,将不符合条件的点LCA求出来,变红,然后算距离

ST表求LCA可直接过,复杂度为$O(n \cdot log_n)$。

倍增求LCA复杂度为$O(n \cdot log_n \cdot log_n)$。

倍增求LCA可先根据深度排序,当有两个不符合的点上方的红点不同时则不能通过修改一个点使得所有不符合的点都产生改变,从而剪枝。

ST表。

 #include <bits/stdc++.h>
using namespace std; #define N 100010
#define ll long long
#define INFLL 0x3f3f3f3f3f3f3f3f struct Edge
{
int to, nx; ll w;
inline Edge() {}
inline Edge(int to, int nx, ll w) : to(to), nx(nx), w(w) {}
}edge[N << ]; int t, n, m, q, k;
int isred[N], prered[N], head[N], arr[N], pos;
int rmq[N << ], F[N << ], P[N], deep[N], cnt;
ll dist[N]; inline void Init()
{
memset(head, -, sizeof head); pos = ;
memset(isred, , sizeof isred); deep[] = ;
} inline void addedge(int u, int v, ll w)
{
edge[++pos] = Edge(v, head[u], w); head[u] = pos;
} struct ST
{
int mm[N << ];
int dp[N << ][];
inline void init(int n)
{
mm[] = -;
for (int i = ; i <= n; ++i)
{
mm[i] = ((i & (i - )) == ) ? mm[i - ] + : mm[i - ];
dp[i][] = i;
}
for (int j = ; j <= mm[n]; ++j)
{
for (int i = ; i + ( << j) - <= n; ++i)
{
dp[i][j] = rmq[dp[i][j - ]] < rmq[dp[i + ( << (j - ))][j - ]] ? dp[i][j - ] : dp[i + ( << (j - ))][j - ];
}
}
}
inline int query(int a, int b)
{
if (a > b) swap(a, b);
int k = mm[b - a + ];
return rmq[dp[a][k]] <= rmq[dp[b - ( << k) + ][k]] ? dp[a][k] : dp[b - ( << k) + ][k];
}
}st; inline void DFS(int u, int pre, int prer)
{
F[++cnt] = u;
rmq[cnt] = deep[u];
P[u] = cnt;
prered[u] = prer;
for (int it = head[u]; ~it; it = edge[it].nx)
{
int v = edge[it].to;
if (v == pre) continue;
if (isred[v]) dist[v] = ;
else dist[v] = dist[u] + edge[it].w;
deep[v] = deep[u] + ;
DFS(v, u, isred[v] ? v : prer);
F[++cnt] = u;
rmq[cnt] = deep[u];
}
} inline void Lca_Init(int root, int node_num)
{
cnt = ;
DFS(root, root, );
st.init( * node_num - );
} inline int query_lca(int u, int v)
{
return F[st.query(P[u], P[v])];
} vector <int> v;
inline bool check(ll mid)
{
v.clear();
for (int i = ; i <= k; ++i)
{
if (dist[arr[i]] > mid)
v.emplace_back(arr[i]);
}
if (v.empty()) return true;
int lca = v[];
for (int i = , len = v.size(); i < len; ++i)
lca = query_lca(lca, v[i]);
if (isred[lca]) return false;
for (auto it : v)
{
if (deep[lca] < deep[prered[it]]) return false;
else if (dist[it] - dist[lca] > mid) return false;
}
return true;
} inline void Run()
{
for (scanf("%d", &t); t--; )
{
scanf("%d%d%d", &n, &m, &q); Init();
for (int i = , u; i <= m; ++i)
{
scanf("%d", &u);
isred[u] = ;
}
ll w;
for (int i = , u, v; i < n; ++i)
{
scanf("%d%d%lld", &u, &v, &w);
addedge(u, v, w); addedge(v, u, w);
}
Lca_Init(, n);
for (int qq = ; qq <= q; ++qq)
{
scanf("%d", &k);
for (int i = ; i <= k; ++i) scanf("%d", arr + i);
if (k == )
{
puts("");
continue;
}
ll l = , r = INFLL, ans;
while (r - l >= )
{
ll mid = (l + r) >> ;
if (check(mid))
{
ans = mid;
r = mid - ;
}
else
l = mid + ;
}
printf("%lld\n", ans);
}
}
} int main()
{
#ifdef LOCAL
freopen("Test.in", "r", stdin);
#endif Run();
return ;
}

倍增求LCA。

 #include<bits/stdc++.h>

 using namespace std;

 typedef long long ll;

 const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const ll MOD = (int)1e9 + ;
const int maxn = (int)1e5 + ; const int DEG = ; struct Edge {
int to, nxt;
ll w;
Edge() {}
Edge(int to, int nxt, ll w) :to(to), nxt(nxt), w(w) {}
}edge[maxn << ]; int head[maxn], tot;
int red[maxn]; void addedge(int u, int v, ll w)
{
edge[tot] = Edge(v, head[u], w); head[u] = tot++;
} void Init(int n)
{
for (int i = ; i <= n; ++i)
{
red[i] = ;
head[i] = -;
}
tot = ;
} ll dis[maxn];
int fa[maxn][DEG];
int deg[maxn];
int pre[maxn]; void BFS(int root)
{
queue<int>q;
dis[root] = ;
deg[root] = ;
fa[root][] = root;
pre[root] = root;
q.push(root);
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int i = ; i < DEG; ++i)
{
fa[tmp][i] = fa[fa[tmp][i - ]][i - ];
}
for (int i = head[tmp]; ~i; i = edge[i].nxt)
{
int v = edge[i].to;
ll w = edge[i].w;
if (v == fa[tmp][]) continue;
deg[v] = deg[tmp] + ;
fa[v][] = tmp;
if (red[v])
{
pre[v] = v;
}
else
{
pre[v] = pre[tmp];
}
if (red[v])
{
dis[v] = ;
}
else
{
dis[v] = dis[tmp] + w;
}
q.push(v);
}
}
} int LCA(int u, int v)
{
if (deg[u] > deg[v]) swap(u, v);
int hu = deg[u], hv = deg[v];
int tu = u, tv = v;
for (int det = hv - hu, i = ; det; det >>= , ++i)
{
if (det & )
{
tv = fa[tv][i];
}
}
if (tu == tv) return tu;
for (int i = DEG - ; i >= ; --i)
{
if (fa[tu][i] == fa[tv][i]) continue;
tu = fa[tu][i], tv = fa[tv][i];
}
return fa[tu][];
} int n, m, q, k;
int arr[maxn]; bool cmp(int a, int b)
{
return dis[a] > dis[b];
} bool check(ll mid)
{
int root = arr[];
int cnt = ;
for (int i = ; i <= k; ++i)
{
if (dis[arr[i]] > mid)
{
if (pre[root] != pre[arr[i]]) return false;
root = LCA(root, arr[i]);
cnt++;
}
}
if (cnt == || cnt == ) return true;
for (int i = ; i <= k; ++i)
{
if (dis[arr[i]] > mid)
{
if (dis[arr[i]] - dis[root] > mid) return false;
}
}
return true;
} void RUN()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d %d %d", &n, &m, &q);
Init(n);
for (int i = ; i <= m; ++i)
{
int u;
scanf("%d", &u);
red[u] = ;
}
for (int i = ; i < n; ++i)
{
int u, v;
ll w;
scanf("%d %d %lld", &u, &v, &w);
addedge(u, v, w);
addedge(v, u, w);
}
BFS();
while (q--)
{
scanf("%d", &k);
for (int i = ; i <= k; ++i)
{
scanf("%d", arr + i);
}
if (k == )
{
puts("");
continue;
}
sort(arr + , arr + + k, cmp);
ll l = ;
ll r = INFLL;
ll ans = ;
while (r - l >= )
{
ll mid = (r + l) >> ;
if (check(mid))
{
r = mid - ;
ans = mid;
}
else
{
l = mid + ;
}
}
printf("%lld\n", ans);
}
}
} int main()
{
#ifdef LOCAL_JUDGE
freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE RUN(); #ifdef LOCAL_JUDGE
fclose(stdin);
#endif // LOCAL_JUDGE
return ;
}

薛的lca讲课配到题解的更多相关文章

  1. 【算法】RMQ LCA 讲课杂记

    4月4日,应学弟要求去了次学校给小同学们讲了一堂课,其实讲的挺内容挺杂的,但是目的是引出LCA算法. 现在整理一下当天讲课的主要内容: 开始并没有直接引出LCA问题,而是讲了RMQ(Range Min ...

  2. Tarjan & LCA 套题题目题解

    刷题之前来几套LCA的末班 对于题目 HDU 2586 How far away 2份在线模板第一份倍增,倍增还是比较好理解的 #include <map> #include <se ...

  3. POJ 1986 Distance Queries(LCA Tarjan法)

    Distance Queries [题目链接]Distance Queries [题目类型]LCA Tarjan法 &题意: 输入n和m,表示n个点m条边,下面m行是边的信息,两端点和权,后面 ...

  4. 初涉倍增&&LCA【在更】

    一种特殊的枚举算法 什么是倍增 顾名思义,即每一次翻倍增加.那么,这样我们就有了一种$O(logn)$阶的方法处理枚举方面的问题了. 参考:[白话系列]倍增算法 一些题目 [倍增]luoguP1613 ...

  5. 并不对劲的bzoj3626:loj2558:p4211:[LNOI2014]LCA

    题目大意 有一棵有\(n\)(\(n\leq5*10^4\))个点的树,\(q\)(\(q\leq5*10^4\))次询问,每次给出\(l,r,x\)表示询问所有编号在\([l,r]\)的点与点\(x ...

  6. ZOJ Monthly, January 2019

    A: Little Sub and Pascal's Triangle Solved. 题意: 求杨辉三角第n行奇数个数 思路: 薛聚聚说找规律,16说Lucas 答案是 $2^p \;\;p 为 n ...

  7. NOIWC2019游记

    更新完了? ghj1222这个智障因为NOIP考的太菜没有去THUWC和PKUWC,但是NOIWC还是苟进去了 由于已经结束了,好多事实忘了,所以可能不完整 2019/1/23 Wednesday 明 ...

  8. 【Lintcode】088.Lowest Common Ancestor

    题目: Given the root and two nodes in a Binary Tree. Find the lowest common ancestor(LCA) of the two n ...

  9. 「SWTR-04」Sweet Round 04 游记

    比赛链接 由于 \(\texttt{Sweet Round}\) 比赛挺好的(关键不知道为啥\(Unrated\) 开篇总结(大雾):这次比赛题目不错(有思维含量) 尽管我不会做. 我一看 \(T1\ ...

随机推荐

  1. 如何使用JavaScript实现纯前端读取和导出excel文件(转)

    转自小茗同学博客:https://www.cnblogs.com/liuxianan/p/js-excel.html js-xlsx 介绍 由SheetJS出品的js-xlsx是一款非常方便的只需要纯 ...

  2. 牛客 82E 无向图中的最短距离 (bitset,bfs)

    有一个n个点的无向图,有m次查询,每次查询给出一些(xi,yi) 令dist(x,y)表示x和y点在图中最短距离,dist(x,x)=0,如果x,y不连通则dist(x,y) = inf 每次查询图中 ...

  3. 请问IOS中做一个手机网站的app壳复杂吗?

    公司开发了一个平台,手机网站已经做出来了,想开发一个苹果应用app,但公司没人会IOS开发,为了减小成本,现在想直接做一个壳来加载手机网站,请问在ios中复杂吗?是否有相应的控件直接加载url就行? ...

  4. mpeg1、mpeg2和mpeg4标准对比分析和总结

    mpeg1.mpeg2和mpeg4标准对比分析和总结 来源 https://blog.csdn.net/SoaringLee_fighting/article/details/83627824 mpe ...

  5. Apache Log View 5.37破解笔记

    i春秋作家:Sp4ce 之前说过要分享这个日志分析工具[记一次简单的攻击日志分析]的破解版,在破解的路上踩了几个坑,记录分享下. 0×00程序概述 原程序 大小: 2283672 字节文件版本: 5. ...

  6. iOS分类(category),类扩展(extension)—史上最全攻略

    背景: 在大型项目,企业级开发中多人同时维护同一个类,此时程序员A因为某项需求只想给当前类currentClass添加一个方法newMethod,那该怎么办呢? 最简单粗暴的方式是把newMethod ...

  7. 阿里高级架构师教你如何使用Spring Cloud Ribbon重试请求

    在微服务调用中,一些微服务圈可能调用失败,通过再次调用以达到系统稳定性效果,本文展示如何使用Ribbon和Spring Retry进行请求再次重试调用. 在Spring Cloud中,使用load b ...

  8. 搭建nginx文件服务器

    一.安装nginx服务 apt install nginx 二.修改nginx配置文件 cd /etc/nginx/conf.d/ vim download_server.conf server { ...

  9. mongodb索引简介

    上面讲解了数据的查询和索引的简单使用,并且说明索引可以显著的加快查询速度,实际上查询的种类有很多,与之对应的索引的种类也有很多,接下来会与索引一起,在说明索引种类的同时,详细介绍下查询的参数 1.索引 ...

  10. YOLO---YOLOv3 with OpenCV 再使用

    YOLO---YOLOv3 with OpenCV 再使用YOLOv3 with OpenCV官网 @ https://github.com/JackKoLing/opencv_deeplearnin ...