HDU 6251 Inkopolis(2017 CCPC-Final,I题,环套树 + 结论)
题目链接 HDU 6251
题意 给出一个$N$个点$N$条边的无向图。然后给出$M$个操作,每个操作为$(x, y, z)$,表示把连接
$x$和$y$的边的颜色改成$z$。
求这张无向图中所有边的颜色的连通块数量。
首先不难得到这是一个环套树的结构。
首先考虑一棵树的情形。
设$f[i]$为$i$这个结点的所有边中的不同颜色数目。
那么整棵树的所有边的颜色的连通块数量即为$∑f(i) - (n - 1)$
现在把这个结论推广到环套树上。
设$f[i]$为$i$这个结点的所有边中的不同颜色数目。
那么整个图的所有边的颜色的连通块数量即为$∑f(i) - n$
但是有一种特殊情况,若这个环上所有的边的颜色相同,
那么整个图的所有边的颜色的连通块数量为$∑f(i) - (n - 1)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef pair <int, int> PII; const int N = 2e5 + 10; unordered_map <int, int> mp[N], cp; map <PII, int> mp2; map <PII, int> oncircle; vector <int> v[N]; int T; int n, m, cnt; int isroot[N]; int a[N], vis[N]; int father[N]; int f[N]; int ans; int cir; int ca = 0; int getcircle(int x){ vis[x] = 1; for (auto u : v[x]){ if (u == father[x]) continue; father[u] = x; if (vis[u]){ cnt = 0; int w = x; while (w ^ u){ a[++cnt] = w; isroot[w] = cnt; w = father[w]; } a[++cnt] = u; isroot[u] = cnt; return 1; } if (getcircle(u)) return 1; } return 0; } int main(){ scanf("%d", &T); while (T--){ printf("Case #%d:\n", ++ca); scanf("%d%d", &n, &m); rep(i, 0, n + 1) v[i].clear(); rep(i, 0, n + 1) mp[i].clear(); mp2.clear(); oncircle.clear(); cp.clear(); cnt = 0; rep(i, 0, n + 1) a[i] = 0; rep(i, 0, n + 1) f[i] = 0; ans = 0; cir = 0; rep(i, 1, n){ int x, y, z; scanf("%d%d%d", &x, &y, &z); if (x > y) swap(x, y); v[x].push_back(y); v[y].push_back(x); if (mp[x][z] == 0){ ++f[x]; ++mp[x][z]; } else ++mp[x][z]; if (mp[y][z] == 0){ ++f[y]; ++mp[y][z]; } else ++mp[y][z]; mp2[MP(x, y)] = z; } rep(i, 1, n) ans += f[i]; rep(i, 0, n + 1) vis[i] = 0; getcircle(1); a[++cnt] = a[1]; rep(i, 1, cnt - 1){ int x = a[i], y = a[i + 1]; if (x > y) swap(x, y); oncircle[MP(x, y)] = 1; } for (auto u : oncircle){ int tt = mp2[MP(u.fi.fi, u.fi.se)]; if (cp[tt] == 0){ ++cir; ++cp[tt]; } else ++cp[tt]; } while (m--){ int x, y, z; scanf("%d%d%d", &x, &y, &z); if (x > y) swap(x, y); int old = mp2[MP(x, y)]; --mp[x][old]; if (mp[x][old] == 0) --f[x], --ans; --mp[y][old]; if (mp[y][old] == 0) --f[y], --ans; if (oncircle.count(MP(x, y)) > 0){ --cp[old]; if (cp[old] == 0) --cir; } mp2[MP(x, y)] = z; if (mp[x][z] == 0){ ++mp[x][z]; ++f[x]; ++ans; } else ++mp[x][z]; if (mp[y][z] == 0){ ++mp[y][z]; ++f[y]; ++ans; } else ++mp[y][z]; if (oncircle.count(MP(x, y)) > 0){ if (cp[z] == 0){ ++cp[z]; ++cir; } else ++cp[z]; } if (cir == 1) printf("%d\n", ans - n + 1); else printf("%d\n", ans - n); } } return 0; }