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 k−1.
我们需要判出所有的简单环,但是我们需要解决的难题是:
- 形如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;
}