【UOJ#389】【UNR#3】白鸽(欧拉回路,费用流)

uoj传送门

题意:
二维平面上给出一些点,同时给出一些无向边连接两个点。
现在从\(1\)号点出发,经过每条边一次,最终回到\(1\)号点。问最终绕原点顺时针旋转最多多少圈。

思路:

  • 直接思考绕原点顺时针转圈不好考虑,因为最终走的一定是一个闭合图形,所以我们可以随便找一条射线,最终顺时针经过这条射线的次数减去逆时针经过这条射线的次数即为答案。
  • 判断是否存在欧拉回路很简单。
  • 接下来我们就来找最大圈数。我们首先贪心给边定向,即每条边都是顺时针走,但这显然最后很可能走不出欧拉回路。
  • 那么我们需要将一些边反向,代价即为这条边对答案的贡献乘以\(2\)。
  • 最后根据度数连接源点汇点,跑个最小费用流就行了。

至于为什么最后需要得到最大流才行,因为我们一定需要将所规定数量的边进行反向,这时我们同时还要得到最小代价,所以就是最大流最小费用了。
这跟这道题很像,就是多了个费用。
注意一下,可能有一些孤立的点存在,这时也可能存在欧拉回路。

#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
//#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;

int n, m;
int x[N], y[N];

int head[N],vis[N],d[N],a[N],pa[N],pre[N];
struct Edge{
    int v,next,c,w;
}e[N];
int tot ;
void adde(int u,int v,int c,int w){
    e[tot].v=v;e[tot].next=head[u];e[tot].w=w;e[tot].c=c;head[u]=tot++;
    e[tot].v=u;e[tot].next=head[v];e[tot].w=-w;e[tot].c=0;head[v]=tot++;
}
int spfa(int s,int t,int &flow,int &cost){
    for(int i=0;i<=t;i++) d[i]=a[i]=INF;d[s]=0;
    memset(vis,0,sizeof(vis));vis[s]=1;
    memset(pre,-1,sizeof(pre));memset(pa,-1,sizeof(pa));
    queue <int> q;q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(e[i].c>0 && d[v]>d[u]+e[i].w){
                d[v]=d[u]+e[i].w;
                pa[v]=u;pre[v]=i;
                a[v]=min(a[u],e[i].c);
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(d[t]==INF) return 0;
    flow+=a[t];
    cost+=a[t]*d[t];
    for(int i=t;i!=-1;i=pa[i]){
        int edge = pre[i];
        e[edge].c-=a[t];
        e[edge^1].c+=a[t];
    }
    return 1;
}
int Min_cost(int s,int t){
    int flow=0,cost=0;
    while(spfa(s,t,flow,cost));
    return cost;
}

int f[N];
int find(int x) {return f[x] == x ? f[x] : f[x] = find(f[x]);}
void Union(int x, int y) {
    int fx = find(x), fy = find(y);
    if(fx != fy) f[fx] = fy;
}
int deg[N];
void run() {
    for(int i = 1; i <= n; i++) {
        cin >> x[i] >> y[i];
    }
    memset(head, -1, sizeof(head));
    int ans = 0;
    for(int i = 1; i <= n; i++) f[i] = i;
    for(int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        if(1ll * x[u] * y[v] > 1ll * x[v] * y[u]) swap(u, v);
        //u -> v
        ++deg[u], --deg[v];
        Union(u, v);
        int val = (y[u] > 0 && y[v] <= 0);
        adde(u, v, 1, 2 * val);
        ans += val;
        vis[u] = vis[v] = true;
    }
    for(int i = 1; i <= n; i++) {
        if(vis[i] && find(i) != find(1)) {
            cout << -1 << '\n';
            return;
        }
    }
    for(int i = 1; i <= n; i++) {
        if(deg[i] & 1) {
            cout << -1 << '\n';
            return;
        }
    }
    int S = 0, T = n + 1;
    for(int i = 1; i <= n; i++) {
        if(deg[i] > 0) {
            adde(S, i, deg[i] / 2, 0);
        } else if(deg[i] < 0) {
            adde(i, T, -deg[i] / 2, 0);
        }
    }
    ans -= Min_cost(S, T);
    cout << ans << '\n';
}

int main() {
//    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(cin >> n >> m) run();
    return 0;
}
上一篇:「UNR#2」黎明前的巧克力


下一篇:【UNR #2】黎明前的巧克力 解题报告