Description
给定一张仙人掌图,第 i 条边连接 u,v,边权为 i
定义路径为 “Happy Path” 当且仅当其满足沿途边权递增。
定义点对 (u,v) Happy 当且仅当存在一条 Happy Path 以 u 为起点,v 为终点。
对于 u=1,2...n,求满足 (u,v) Happy 的 v 的数量。
n,m≤5×105。
Solution
先考虑树的情况怎么做。
由于这里建圆方树跑 dp 非常复杂,考虑按照编号从大到小加边。
设 fi 表示 i 走已经加入的边能到多少个点。那么假设加入了 (u,v),因为 (u,v) 是当前边权最小的边,所以 u 和 v 跨过这条边后可以随便走,即让 fu=fv=fu+fv。
对于一般仙人掌的情况,直接照搬上面那个做法会出现一个问题,就是 (u,v) 在加入这条边之前就已经存在共同能够到达的点了,会算重。
注意到这是个仙人掌,所以如果算重,就一定要满足 (u,v) 是其所在环的最小边,且 u,v 分别能够到达所在环的最大边 (u0,v0),这时 u 和 v 同时能到达 u0,v0 和通过 (u0,v0) 能到的所有点。
算重的部分在加入 (u0,v0) 的时候记录 fu0+fv0 即可,所有环可以在 dfs 树上求出。
时间复杂度:O(n+m)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include <bits/stdc++.h>
const int kMaxN = 5e5 + 5;
int n, m; int u[kMaxN], v[kMaxN], dep[kMaxN], p[kMaxN], mat[kMaxN]; int f[kMaxN], g[kMaxN]; std::vector<int> G[kMaxN];
void dfs(int u, int pid) { p[u] = pid; for (auto id : G[u]) { int v = ::u[id] ^ ::v[id] ^ u; if (!dep[v]) { dep[v] = dep[u] + 1, dfs(v, id); } else if (dep[v] > dep[u]) { std::vector<int> ver, ed; for (int i = v; i != u; i = ::u[p[i]] ^ ::v[p[i]] ^ i) ver.emplace_back(i), ed.emplace_back(p[i]); ver.emplace_back(u), ver.emplace_back(v), ed.emplace_back(id); int mx = std::max_element(ed.begin(), ed.end()) - ed.begin(), mi = std::min_element(ed.begin(), ed.end()) - ed.begin(); if ((int)ed.size() == 2) { mat[ed[mi]] = ed[mx]; continue; } bool fl = 1; for (int i = (mx + 1) % ed.size(); i != mi; i = (i + 1) % ed.size()) { fl &= (ed[i] < ed[(i + (int)ed.size() - 1) % ed.size()]); } for (int i = (mx + (int)ed.size() - 1) % ed.size(); i != mi; i = (i + (int)ed.size() - 1) % ed.size()) { fl &= (ed[i] < ed[(i + 1) % ed.size()]); } if (fl) mat[ed[mi]] = ed[mx]; } } }
void dickdreamer() { std::cin >> n >> m; for (int i = 1; i <= m; ++i) { std::cin >> u[i] >> v[i]; G[u[i]].emplace_back(i), G[v[i]].emplace_back(i); } dep[1] = 1, dfs(1, 0); std::fill_n(f + 1, n, 1); for (int i = m; i; --i) { g[i] = f[u[i]] = f[v[i]] = f[u[i]] + f[v[i]] - g[mat[i]]; } for (int i = 1; i <= n; ++i) std::cout << f[i] - 1 << ' '; }
int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; while (T--) dickdreamer(); return 0; }
|