1. 程式人生 > >【題解】CQOI2017老C的方塊

【題解】CQOI2017老C的方塊

十分 方塊 IT 網絡流 其他 ems space 題意 白色

  網絡流真的是一種神奇的算法。在一張圖上面求感覺高度自動化的方案一般而言好像都是網絡流的主陣地。講真一開始看到這道題也有點懵,題面很長,感覺很難的樣子。不過,仔細閱讀了題意之後明白了:我們所要做的就是要用最小的代價,使得最後的圖中不能出現給定的四種圖案。

  實際上之前做過一道非常毒瘤的網絡流題目【無限之環】。當時就思考了一下:為什麽出題人規定不能旋轉直線管子?原因就是因為這樣的圖建不出來,它與不具有其他的管子的特點。那麽可以斷定:給出的這四個圖案必然也是非常特殊的圖形,否則不會只有這四個/不會是這四個。於是觀察這四個圖案,不難發現它們的特征:以一對中間夾了特殊邊的方塊為中心,分別有另一方塊隨意連接在上方。

  我們從這個中心開始入手:由於要求最小值,所以-->最小割 / 費用流。但一個直觀的感覺,它是在很多的方案當中做出選擇,與最小割是比較貼合的。(每一種圖案牽扯到四個方塊,拿掉任何一個就可以破壞這個圖形)如果每一種方案彼此平行,只需建出圖暴力跑即可。可是會有交叉:當我們刪去一個方塊時,可能同時破壞了兩個方案。

  觀察圖案,由於上下,左右交錯,一個中心只可能出現在一個方案中,但其兩側的方塊卻可能出現在不同的方案當中。於是我們對於這樣兩側的方塊進行黑白染色,保證每一種方案當中所牽涉到的另兩個方塊分別屬於不同的顏色(由於題目的特殊性質是可以做到的)。

  那麽建邊的方式也十分的自然了:

  源點-->所有白色的點,邊權為代價;所有白色的點-->中心連邊,邊權為INF;所有的中心 = 兩條邊 = 邊權為各個方塊的代價。所有的中心 --> 黑點,黑點 --> 匯點,邊權為代價。此時的最小割所代表的意義即為我們做出的最小代價方案選擇。

  做出這題還是比較開心的,代碼跑得也很快。不過細節比較多,要註意一下……

// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
#define maxn 300000
#define maxm 800000
#define
ll long long #define INF 9999999 int C, R, n, S, T, tot; int Color[maxn], cnt; int cnp = 2, head[maxn], cur[maxn]; int lev[maxn]; map <int, int> Map; int dx[10] = {1, 1, 1, 1, 0, 0, -1, -1, -1, -1}; int dy[10] = {-1, 0, 1, 2, -1, 2, -1, 0, 1, 2}; struct edge { int to, last, f; }E[maxm]; struct node { int x, y, w; }P[maxn]; bool cmp(node a, node b) { if(a.x != b.x) return a.x < b.x; return a.y < b.y; } int read() { int x = 0, k = 1; char c; c = getchar(); while(c < 0 || c > 9) { if(c == -) k = -1; c = getchar(); } while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); return x * k; } void add(int u, int v, int f) { E[cnp].to = v, E[cnp].f = f, E[cnp].last = head[u], head[u] = cnp ++; E[cnp].to = u, E[cnp].f = 0, E[cnp].last = head[v], head[v] = cnp ++; } bool Bfs() { queue <int> q; memset(lev, 0, sizeof(lev)); q.push(S); lev[S] = 1; while(!q.empty()) { int u = q.front(); q.pop(); for(int i = head[u]; i; i = E[i].last) { int v = E[i].to; if(!lev[v] && E[i].f) { lev[v] = lev[u] + 1; q.push(v); } } if(lev[T]) return 1; } return 0; } int Dfs(int u, int nf) { if(u == T) return nf; int lf = 0; for(int i = cur[u]; i; i = E[i].last) { int v = E[i].to; if(lev[v] == lev[u] + 1 && E[i].f) { int af = Dfs(v, min(E[i].f, nf)); nf -= af, lf += af; E[i].f -= af, E[i ^ 1].f += af; if(!nf) return lf; cur[u] = i; } } if(!lf) lev[u] = -1; return lf; } int Dinic() { int ans = 0; while(Bfs()) { memcpy(cur, head, sizeof(head)); ans += Dfs(S, INF); } return ans; } int id(int x, int y) { ll k = 1ll * R * (x - 1) + 1ll * y; if(Map[k]) return Map[k]; else return Map[k] = ++ tot; } int Check(int x, int y) { ll k = 1ll * R * (x - 1) + 1ll * y; if(Map[k]) return Map[k]; else return 0; } void Get_Graph(int p) { int a = ++ tot, b = ++ tot, c = ++ tot; add(a, b, P[p].w); add(b, c, P[p + 1].w); for(int i = 0; i < 10; i ++) { int xx = P[p].x + dx[i], yy = P[p].y + dy[i], tem; if(xx < 1 || xx > C || yy < 1 || yy > R) continue; if(tem = Check(xx, yy)) { if(Color[tem] == 2) add(tem, a, INF); else add(c, tem, INF); } } } int main() { C = read(), R = read(); swap(C, R); n = read(); S = 0, T = 2 * n + 100; for(int i = 1; i <= n; i ++) { int x = read(), y = read(), w = read(); swap(x, y); if((x & 1) && ((!((y - 1) % 4)) || (!((y - 2) % 4)))) { P[++ cnt].x = x, P[cnt].y = y; P[cnt].w = w; continue; } if((!(x & 1)) && ((!((y - 3) % 4)) || (!((y - 4) % 4)))) { P[++ cnt].x = x, P[cnt].y = y; P[cnt].w = w; continue; } if(x & 1) { if(((y - 3) % 4)) add(S, id(x, y), w), Color[id(x, y)] = 2; else add(id(x, y), T, w), Color[id(x, y)] = 1; } else { if(!((y - 2) % 4)) add(id(x, y), T, w), Color[id(x, y)] = 1; else add(S, id(x, y), w), Color[id(x, y)] = 2; } } sort(P + 1, P + 1 + cnt, cmp); for(int i = 1; i <= cnt; i += 1) { if(P[i + 1].x != P[i].x) continue; if(P[i + 1].y != P[i].y + 1) continue; Get_Graph(i); } printf("%d\n", Dinic()); return 0; }

【題解】CQOI2017老C的方塊