1. 程式人生 > 其它 >UOJ389【UNR #3】白鴿【計算幾何,網路流】

UOJ389【UNR #3】白鴿【計算幾何,網路流】

給定 \(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);
}