0%

[CQOI2017]老C的方块

题面描述

在棋盘内破坏指定联通块的最小代价

需要破坏的联通块

题解

观察联通块,我们发现可以一个需要破坏的联通块是以蓝线为中心的八联通块,其中蓝线两边必须有2个方块,除此之外,蓝线两边至少要有1个方块

其次,我们知道破坏棋盘问题网络流一般是黑白染色

但是这道题无法黑白染色,因为无法体现蓝线的性质,所以我们采取如下的染色

染色方案

我们发现这样染色可以很好体现刚才分析的性质

染色后的联通块

蓝线恰好位于红蓝两色之前,除了红蓝两色以外,两边各有3个不同颜色的块

我们考虑建模表示破坏联通块的方法

断掉联通块后不连通,实际上就是就是求最小割

破坏的最小代价就是三绿,三黄,红,蓝最小值

网络流中并联表示或,串联表示与

因此采用如下建图

源点并联黄点 容量为黄点权值

绿点并联汇点 容量为绿点权值

黄点连红点 蓝点连绿点 容量为INF

红点连蓝点 容量为红蓝最小值

至于染色判断,找规律即可

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <queue>
#include <map>
using namespace std;
inline int read(int f = 1, int x = 0, char ch = ' ')
{
while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) x = x*10+ch-'0', ch = getchar();
return f*x;
}
const int N = 6e6+5, INF = 0x3f3f3f3f;
const int dx[4] = {1, -1, 0, 0};
const int dy[4] = {0, 0, 1, -1};
struct Edge
{
int next, to, w;
Edge(int next = 0, int to = 0, int w = 0): next(next), to(to), w(w) {};
}edge[N];
int tot = 1, head[N], s, t, n, m;
void _add(int x, int y, int z) { edge[++tot] = Edge(head[x], y, z); head[x] = tot; }
void add(int x, int y, int z) { _add(x, y, z); _add(y, x, 0); }
int d[N];
bool bfs()
{
memset(d, 0, sizeof(d));
queue<int> q; q.push(s); d[s] = 1;
while(q.size())
{
int x = q.front(); q.pop();
for(int i = head[x]; i; i = edge[i].next)
{
int y = edge[i].to, z = edge[i].w;
if(!z||d[y]) continue;
d[y] = d[x]+1; q.push(y);
if(y == t) return true;
}
}
return false;
}
int dinic(int x, int flow)
{
if(x == t) return flow;
int rest = flow, k;
for(int i = head[x]; i&&rest; i = edge[i].next)
{
int y = edge[i].to, z = edge[i].w;
if(!z||d[y] != d[x]+1) continue;
k = dinic(y, min(z, rest));
edge[i].w -= k; edge[i^1].w += k; rest -= k;
if(!k) d[y] = 0;
}
return flow-rest;
}

map<pair<int, int> , int> h;
int r, c, col[N], px[N], py[N], w[N];
/*
x%4 == 1&&y%2 == 1 = red x%4 == 0&&y%2 == 0 = red
x%4 == 1&&y%2 == 0 = yellow x%4 == 0&&y%2 == 1 = yellow
x%4 == 2&&y%2 == 1 = blue x%4 == 3&&y%2 == 0 = blue
x%4 == 2&&y%2 == 0 = green x%4 == 3&&y%2 == 1 = green
蓝线恰好在红块与蓝块之间
源点并联黄点 绿点并联汇点
黄点连红点 蓝点连绿点 红点连蓝点
*/

void addpurple(int x)
{
int _x = px[x], _y = py[x];
for(int i = 0; i < 2; ++i)
{
int nx = _x+dx[i], ny = _y;
if(!h.count(make_pair(nx, ny))) continue;
int y = h[make_pair(nx, ny)];
if(col[y] == 3) add(x, y, min(w[x], w[y]));
}
}

void addyellow(int x)
{
int _x = px[x], _y = py[x];
add(s, x, w[x]);
for(int i = 0; i < 4; ++i)
{
int nx = _x+dx[i], ny = _y+dy[i];
if(!h.count(make_pair(nx, ny))) continue;
int y = h[make_pair(nx, ny)];
if(col[y] == 1) add(x, y, INF);
}
}

void addgreen(int x)
{
int _x = px[x], _y = py[x];
add(x, t, w[x]);
for(int i = 0; i < 4; ++i)
{
int nx = _x+dx[i], ny = _y+dy[i];
if(!h.count(make_pair(nx, ny))) continue;
int y = h[make_pair(nx, ny)];
if(col[y] == 3) add(y, x, INF);
}
}
int maxflow, flow;
int main()
{
c = read(), r = read(), n = read(); s = n+1; t = n+2;
for(int i = 1; i <= n; ++i)
{
px[i] = read(), py[i] = read(), w[i] = read();
h[make_pair(px[i], py[i])] = i;
}
for(int i = 1; i <= n; ++i)
{
int x = px[i], y = py[i];
if((x%4 == 1&&y%2 == 1)||(x%4 == 0&&y%2 == 0)) col[i] = 1;
if((x%4 == 1&&y%2 == 0)||(x%4 == 0&&y%2 == 1)) col[i] = 2;
if((x%4 == 2&&y%2 == 1)||(x%4 == 3&&y%2 == 0)) col[i] = 3;
if((x%4 == 2&&y%2 == 0)||(x%4 == 3&&y%2 == 1)) col[i] = 4;
}
for(int x = 1; x <= n; ++x)
{
if(col[x] == 1) addpurple(x);
else if(col[x] == 2) addyellow(x);
else if(col[x] == 4) addgreen(x);
}
while(bfs()) while(flow = dinic(s, INF)) maxflow += flow;
printf("%d\n", maxflow);
return 0;
}

图片来源

特别感谢 shadowice1984