HDU6200 mustedge mustedge mustedge (2017 ACM/ICPC Asia Regional Shenyang Online)

传送门

题目大意

给出一张连通图,现在有两种操作:
1 u v :向图中加入一条连接 u v的边
2 u v :询问 u v之间的必经边的条数

思路

首先随便找到一个生成树,每条边的初始权值为1,对于所有非树边 (u,v) ,将生成树上 u v的路径上的所有边的权值都变为0,那么询问时只需要回答 u v的边权和即可,这就很容易了。
然后考虑加边操作,因为每一条边只会被删除一次,所以我们可以记录每一个节点在它到根的路径上离它最近的一个没有被删除的边,通过并查集维护。

代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int n, m, q, t[MAXN], f[MAXN], fa[MAXN], adj[MAXN], c, out[MAXN], dfn[MAXN];
int edge[MAXN][2], dcnt, sz[MAXN], hsn[MAXN], htp[MAXN], dep[MAXN];
struct { int v, nxt; } e[MAXN << 1];
inline void Addedge(int u, int v) {
    e[++ c].v = v; e[c].nxt = adj[u]; adj[u] = c;
}
inline void GET(int&n) {
    static char c; n = 0;
    do c = getchar(); while('0' > c || c > '9');
    while('0' <= c && c <= '9') { n = n * 10 + c - '0'; c = getchar(); }
}
inline void Add(int x, int v) {
    for(; x <= n; x += x&-x) t[x] += v;
}
inline int Qsum(int x) {
    int ans = 0;
    for(; x > 0; x -= x&-x) ans += t[x];
    return ans;
}
int Find(int x) { return (x == f[x]) ? x : (f[x] = Find(f[x])); }
void dfs(int u) {
    dep[u] = dep[fa[u]] + 1; dfn[u] = ++ dcnt; sz[u] = 1; hsn[u] = 0;
    for(int i = adj[u]; i; i = e[i].nxt) if(e[i].v != fa[u]) {
        fa[e[i].v] = u;
        dfs(e[i].v); sz[u] += sz[e[i].v];
        if(sz[e[i].v] > sz[hsn[u]]) hsn[u] = e[i].v;
    }
    out[u] = dcnt;
}
void dfs2(int u, int tp) {
    htp[u] = tp;
    if(hsn[u]) dfs2(hsn[u], tp);
    for(int i = adj[u]; i ; i = e[i].nxt)
        if(e[i].v != fa[u] && e[i].v != hsn[u])
            dfs2(e[i].v, e[i].v);
}
int LCA(int u, int v) {
    while(htp[u] != htp[v]) {
        if(dep[htp[u]] < dep[htp[v]]) swap(u, v);
        u = fa[htp[u]];
    }
    return dep[u] < dep[v] ? u : v;
}
void Adde(int u, int v) {   //to add not_in_tree edge
    int lca = LCA(u, v), a = u, b = v, x;
    for(; dep[(x = Find(a))] > dep[lca]; a = fa[x]) {
        Add(dfn[x], -1); Add(out[x]+1, 1); f[x] = Find(fa[x]);
    }
    for(; dep[(x = Find(b))] > dep[lca]; b = fa[x]) {
        Add(dfn[x], -1); Add(out[x]+1, 1); f[x] = Find(fa[x]);
    }
}
int Query(int u) {
    return dep[u] + Qsum(dfn[u]);
}
int main() {
    int T, u, v, cnt, op; cin >> T;
    for(int Cas = 1; Cas <= T; ++ Cas) {
        cin >> n >> m; cnt = dcnt = c = 0;
        for(int i = 1; i <= n; ++ i) f[i] = i, adj[i] = 0, t[i] = 0;
        for(int i = 1; i <= m; ++ i) {
            GET(u); GET(v);
            if(Find(u) != Find(v)) {
                Addedge(u, v);
                Addedge(v, u);
                f[Find(u)] = Find(v);
            } else {
                edge[++ cnt][0] = u;
                edge[cnt][1] = v;
            }
        }
        fa[1] = 0;
        dfs(1); dfs2(1, 1);
        for(int i = 1; i <= n; ++ i) f[i] = i;
        for(int i = 1; i <= cnt; ++ i)
            Adde(edge[i][0], edge[i][1]);
        scanf("%d", &q);
        printf("Case #%d:\n", Cas);
        for(int i = 1; i <= q; ++ i) {
            GET(op); GET(u); GET(v);
            if(op == 1) Adde(u, v);
            else printf("%d\n", Query(u) + Query(v) - 2 * Query(LCA(u, v)));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值