题意:一个可能有重边、自环的无向图,现在一些点损坏了。给定一些已知的点,且保证这些点没有损坏,但是这些点均不能通过没有损坏的点到达标号为1的点。试求最少的不能够通过没有损坏的点到达标号为1的点的数目(损坏的点也算)。
Sol:
一开始想的是求割点+树形dp,后来发现沙茶了。
首先发现一个性质,对于给定已知点p,若存在路径p->u,那么必然有点u损坏或点u不能通过没有损坏的点到达标号为1的点。
那么在最优意义下,如何满足这些限制呢?
一种显然可行的方法为:将与给定点相邻的点全部标记为损坏。我们容易证明其满足上述性质。
事实上,这是一种最优的选择。
怎么证明呢。我们可以想象,损坏点以给定点为中心形成了一圈轮廓,那么轮廓内的点显然全部是不可达的。那么我们只需要让轮廓尽量小就好了。那么最小的轮廓恰是上述的选择方式。
由于显然两个给定点之间的处理不相互影响,这样做是正确的。
那么标记过后flood-fill一下就行了。
#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 30010
#define M 100010
int head[N], next[M << 1], end[M << 1];
void addedge(int a, int b) {
static int q = 1;
end[q] = b;
next[q] = head[a];
head[a] = q++;
}
void make(int a, int b) {
addedge(a, b);
addedge(b, a);
}
bool cut[N];
bool vis[N];
void dfs(int x) {
vis[x] = 1;
for(int j = head[x]; j; j = next[j])
if (!cut[end[j]] && !vis[end[j]])
dfs(end[j]);
}
int main() {
int n, m, p;
scanf("%d%d%d", &n, &m, &p);
register int i, j;
int a, b, x;
for(i = 1; i <= m; ++i) {
scanf("%d%d", &a, &b);
make(a, b);
}
while(p--) {
scanf("%d", &x);
for(j = head[x]; j; j = next[j])
cut[end[j]] = 1;
}
dfs(1);
int ans = n;
for(i = 1; i <= n; ++i)
if (vis[i])
--ans;
printf("%d", ans);
return 0;
}