12.04 CW 模拟赛 T2.树的链接
算法
全都想到了, 不会读入和 \(\rm{LCA}\)
直接把赛时记录的拉过来
对于 \(50 \%\) 的数据点, 直接输出 \(-1\) 即可
前 \(20 \%\) 直接预处理即可
注意到一个很强的性质, 即
保证在此之前在城市 \(x\) 与 \(y\) 之间不存在任何路径
也就是说每次连边都是加入割边, 最终的图一定是一个森林
并查集处理, 同连通块之间相当于一颗树, 把最终的树建出来之后, 离线的去处理询问即可, 具体的, 对于最终的树和询问时的树, 只要他们在询问时已经联通, 那么最短路不会发生变化, 即 \(\rm{LCA}\) 不变
时间复杂度 \(\mathcal{O} (q + n \log n + q \log n)\)
至此, 可以解决本题
不是哥们 \(\rm{T2}\) 比 \(\rm{T1}\) 简单多少????
框架
读入的时候边建树边判断 \(-1\) , 建完之后跑 \(\rm{LCA}\) 逐个处理
代码
让我写一写, 第一次写神秘题目
不过把后面的看完再说, 让我看看 \(\rm{T3}\) 怎么个事
#include <bits/stdc++.h>
const int MAXN = 5e5 + 50;
namespace IO {
inline bool read(int &x) // FALSE 表示没有接下来的了
{
int f = 1;
x = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
x = x * 10 + ch - '0', ch = getchar();
return ch == ' ';
}
}
using namespace IO;
int n, q;
class Sol_Class
{
private:
struct DSU_Struct
{
int fa[MAXN];
void init() {
#pragma GCC unroll 8
for (int i = 1; i <= n; ++i) fa[i] = i;
}
int find(int x) { return fa[x] = (x == fa[x] ? fa[x] : find(fa[x])); }
inline void merge(int u, int v) {
int fau = find(u), fav = find(v);
if (fau != fav) fa[fav] = fau;
}
} DSU; // 并查集
struct node
{
int to, w;
int next;
} Edge[MAXN << 1];
int Edge_cnt = 0, head[MAXN];
inline void addedge(int u, int v, int w) {
Edge[Edge_cnt + 1].next = head[u];
head[u] = Edge_cnt + 1;
Edge[Edge_cnt + 1].to = v, Edge[Edge_cnt + 1].w = w;
Edge_cnt++;
}
struct ques
{
int type; // 1 表示加边, 0 表示查询
int x, y, w;
bool tag; // 表示是否有解
} Q[MAXN];
int dep[MAXN]; // 深度
int listval[MAXN]; // 处理根节点到每个点的距离
int fa[MAXN][30]; // 倍增的祖宗
/*离线处理询问*/
void offline()
{
DSU.init();
#pragma GCC unroll 8
for (int i = 1; i <= n; ++i) head[i] = -1, listval[i] = 0, dep[i] = -1;
#pragma GCC unroll 8
for (int i = 1; i <= q; ++i) {
int x, y, w; read(x), read(y) ? read(w) : w = -1;
if (~w) {
Q[i].type = 1, Q[i].x = x, Q[i].y = y, Q[i].w = w;
DSU.merge(x, y);
addedge(x, y, w), addedge(y, x, w);
} else {
Q[i].type = 0, Q[i].x = x, Q[i].y = y, Q[i].w = w;
if (DSU.find(x) != DSU.find(y)) Q[i].tag = false;
else Q[i].tag = true;
}
}
}
/*处理 dep , listval, fa*/
void dfs1(int Now, int fat, int w) {
dep[Now] = dep[fat] + 1;
listval[Now] = listval[fat] + w;
/*处理 fa*/
fa[Now][0] = fat;
for (int i = 1; (1 << i) <= dep[Now]; ++i)
fa[Now][i] = fa[fa[Now][i - 1]][i - 1];
for (int i = head[Now]; ~i; i = Edge[i].next) {
int NowTo = Edge[i].to, NowW = Edge[i].w;
if (NowTo == fat) continue;
dfs1(NowTo, Now, NowW);
}
}
/*处理倍增信息*/
void LCA_init()
{
#pragma GCC unroll 8
for (int i = 1; i <= n; ++i) {
if (~dep[i]) continue;
dfs1(i, 0, 0);
}
}
/*处理 x, y 的 LCA 点的位置*/
int LCA(int x, int y)
{
if (dep[x] < dep[y]) std::swap(x, y);
for (int i = 19; i >= 0; i--)
if (dep[x] - (1 << i) >= dep[y])
x = fa[x][i];
if (x == y) return x;
for (int i = 19; i >= 0; i--)
if (fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
public:
void solve()
{
scanf("%d %d", &n, &q);
offline();
LCA_init();
#pragma GCC unroll 8
for (int i = 1; i <= q; ++i) {
if (Q[i].type == 1) continue;
if (!Q[i].tag) printf("-1\n");
else {
int lca = LCA(Q[i].x, Q[i].y);
printf("%d\n", -(2 * listval[lca] - listval[Q[i].x] - listval[Q[i].y]));
}
}
}
} Sol;
int main()
{
Sol.solve();
return 0;
}
总结
没啥好说的, 简单题, 打不出来真抽象

浙公网安备 33010602011771号