UOJ389【UNR #3】白鴿【計算幾何,網路流】
阿新 • • 發佈:2021-06-25
給定 \(n\) 個點 \(m\) 條邊的無自環的無向圖,每個點有座標 \((x_i,y_i)\),求經過 \(1\) 的歐拉回路關於原點卷繞數的最大值,需判斷無解。
\(n,m\le 2\cdot 10^4\),\(|x_i|,|y_i|\le 10^9\)。
先判斷是否有解,然後題目即為給邊定向使得每個點的出度 \(=\) 入度,這是(上下界)網路流模型。
簡單的想法是將費用設為極角之差,但因為費用流複雜度跟費用有關所以可能爆炸。
考慮射線法,只有當一條邊越過射線時才記 \(\pm 1\) 權值,就可以了。
容量和費用都比較小的時候多路增廣會快一些,實現方法就是把 dinic(不用當前弧優化)搬過來,並在 dfs 的時候把流乾的點剪枝掉。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 20003, M = N<<2, INF = 0x3f3f3f3f; template<typename T> void rd(T &x){ int ch = getchar(); x = 0; bool f = false; for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-'; for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0'; if(f) x = -x; } template<typename T> bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;} int n, m, x[N], y[N], fa[N], ans, head[N], deg[N], to[M], nxt[M], cap[M], cst[M]; bool vis[N]; void add(int a, int b, int c, int d){ static int cnt = 1; to[++cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; cap[cnt] = c; cst[cnt] = d; to[++cnt] = a; nxt[cnt] = head[b]; head[b] = cnt; cap[cnt] = 0; cst[cnt] = -d; } int getf(int x){return x == fa[x] ? x : fa[x] = getf(fa[x]);} void AMOD(int &x){++ x; if(x == N) x = 0;} void DMOD(int &x){if(!x) x = N; -- x;} int S, T, dis[N], Q[N], fr, re; bool spfa(){ memset(dis, 0x3f, sizeof dis); re = 1; dis[Q[fr = 0] = S] = 0; while(fr != re){ int u = Q[fr]; AMOD(fr); vis[u] = false; for(int i = head[u];i;i = nxt[i]) if(cap[i] && chmin(dis[to[i]], dis[u] + cst[i]) && !vis[to[i]]){ vis[to[i]] = true; if(dis[to[i]] <= dis[Q[fr]]){DMOD(fr); Q[fr] = to[i];} else {Q[re] = to[i]; AMOD(re);} } } return dis[T] < INF; } int dfs(int x, int lim){ if(!lim || x == T) return lim; int flow = 0, f; vis[x] = true; for(int i = head[x];i && lim;i = nxt[i]) if(cap[i] && !vis[to[i]] && dis[to[i]] == dis[x] + cst[i] && (f = dfs(to[i], min(lim, cap[i])))){ flow += f; lim -= f; cap[i] -= f; cap[i^1] += f; } vis[x] = false; if(!flow) dis[x] = INF; return flow; } int main(){ rd(n); rd(m); S = 0; T = n+1; for(int i = 1;i <= n;++ i){ rd(x[i]); rd(y[i]); fa[i] = i; } for(int i = 1, u, v;i <= m;++ i){ rd(u); rd(v); vis[u] = vis[v] = true; fa[getf(u)] = getf(v); if((LL)x[u] * y[v] > (LL)x[v] * y[u]) swap(u, v); if(y[u] >= 0 && y[v] < 0){++ans; add(u, v, 1, 2);} else add(u, v, 1, 0); --deg[u]; ++deg[v]; } for(int i = 1;i <= n;++ i) if(vis[i] && getf(i) != getf(1) || deg[i] & 1){ puts("-1"); return 0; } for(int i = 1;i <= n;++ i) if(deg[i] < 0) add(S, i, -deg[i]>>1, 0); else add(i, T, deg[i]>>1, 0); memset(vis, 0, sizeof vis); while(spfa()) ans -= dis[T] * dfs(S, INF); printf("%d\n", ans); }