BZOJ1574 USACO 2009 Jan Gold 1.Earthquake Damage Solution

题意:一个可能有重边、自环的无向图,现在一些点损坏了。给定一些已知的点,且保证这些点没有损坏,但是这些点均不能通过没有损坏的点到达标号为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;
}



你可能感兴趣的:(BZOJ1574 USACO 2009 Jan Gold 1.Earthquake Damage Solution)