『仙人掌判环·贪心』沙漠点列

mac2025-12-29  6

P r o b l e m \mathrm{Problem} Problem

S o l u t i o n \mathrm{Solution} Solution

显然,仙人掌不存在复杂环,这是这道题解题的关键。

对于割边,我们可以直接删。删一条边,贡献为1.对于简单环,若删 k k k条边,贡献是 k − 1 k-1 k1.

我们需要判出所有的简单环,但是我们需要解决的难题是:

形如8字型的环要判成两个。需要具体求出每一个环的大小。

此时无向图的tarjan算法、割边、并查集都不可以,我们可以尝试DFS。

我们记录图上某一个点在搜索树上的深度,若找到已经访问过的点,深度之差就是环的大小,记得需要特判环的大小大于2,因为不存在2元环(如题,没有重边)。

代码如下:

void dfs(int x,int fa) { for (int i=Link[x];i;i=e[i].next) { int y = e[i].y; if (y == fa) continue; if (!dis[y]) { dis[y] = dis[x] + 1; dfs(y,x); } else if (dis[y] < dis[x] + 1) scc[++cnt] = dis[x]-dis[y]+1; } return; }

然后就随随便便排个序贪心乱搞一下就A拉~

\mathrm{Code}

#include <vector> #include <cstdio> #include <iostream> #include <algorithm> const int N = 1e6 + 10; const int M = 2e6 + 10; int n, m, k, tot(0), cnt(0), ans(0); int dis[N], scc[N], Link[N]; struct node { int y, next; } e[M * 2]; int read(void) { int s = 0, w = 0; char c = getchar(); while (c < '0' || c > '9') w |= c == '-', c = getchar(); while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar(); return w ? -s : s; } void add(int x,int y) { e[++tot] = node{y,Link[x]}; Link[x] = tot; } void dfs(int x,int fa) { for (int i=Link[x];i;i=e[i].next) { int y = e[i].y; if (y == fa) continue; if (!dis[y]) { dis[y] = dis[x] + 1; dfs(y,x); } else if (dis[y] < dis[x] + 1) scc[++cnt] = dis[x]-dis[y]+1; } return; } int main(void) { n = read(), m = read(), k = read(); for (int i=1,x,y;i<=m;++i) { x = read(), y = read(); add(x,y), add(y,x); } for (int i=1;i<=n;++i) { if (dis[i] > 0) continue; dis[i] = 1; dfs(i,0); ans ++; } int edge = m; for (int i=1;i<=cnt;++i) edge -= scc[i]; if (edge >= k) { ans += k; std::cout<<ans; return 0; } ans += edge, k -= edge; std::sort(scc+1,scc+cnt+1); std::reverse(scc+1,scc+cnt+1); for (int i=1;i<=cnt;++i) { if (k - scc[i] >= 0) { k -= scc[i]; ans += scc[i] - 1; } else { ans += k-1; break; } } std::cout<<ans; return 0; }
最新回复(0)