[uoj389][UNR #3]白鸽【欧拉回路】【射线法】【费用流】

【题目链接】
  http://uoj.ac/problem/389
【题解】
  首先存在欧拉回路的条件是所有非孤立点都与一号点连通,并且每个点的度数都是偶数。
  一个简单的想法,把每条边绕原点旋转在的角度记为这条边的费用(如果是负的就把这条边反向),我们先把所有的费用都加在一起作为初始的答案。那么显然会有一些点的度数不符合条件。考虑费用流,对于一条边 (u,v) ( u , v ) 我们从 u u v连一条费用为 2 2 ∗ 这 条 边 费 用 的边,表示将这条边反向。对于 > 入 度 > 出 度 的点,从 S S 向这个点连费用是1,流量为()/2 > 出 度 > 入 度 的点向 T T 连边。跑最小费用流即可。
  由于边权不一定为整数,费用流会跑的比较慢。由于绕的圈数一定是整数,所以可以用射线法:从原点射出一条射线,如果边从左到右跨过费用为1,反之为-1。在新图上做费用流答案就费用是整数了。
  费用流的话,每次spfa求出最短路图,在最短路图上用dinic增广即可。
  时间复杂度:O(NM)?,反正跑的挺快的。
【代码】

# include <bits/stdc++.h>
# define    N     100010
# define    ll    long long
# define    inf     0x3f3f3f3f
# define    eps     1e-7
using namespace std;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9'){if (ch == '-') fh = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9'){tmp = tmp * 10 + ch - '0'; ch = getchar();}
    return tmp * fh;
}
const double pi = acos(-1.0);
struct Node{
  int data, next, l, re;
  double vote;
}e[N];
struct Edge{
  int u, v;
}eg[N];
struct Point{
  double x, y;
}p[N];
int n, m, deg[N], use[N], place, head[N], q[N], frm[N], now[N], step[N];
int dis[N], ans;
void buildedge(int u, int v, int w){
  e[++place].data = v; e[place].next = head[u]; head[u] = place; e[place].l = w;
}
void build(int u, int v, int l, double w){
  e[++place].data = v; e[place].next = head[u]; head[u] = place; e[place].l = l; e[place].vote = w; e[place].re = place + 1;
  e[++place].data = u; e[place].next = head[v]; head[v] = place; e[place].l = 0; e[place].vote = -w; e[place].re = place - 1;
}
double cross(Point x, Point y){ return x.x * y.y - x.y * y.x; }
double dot(Point x, Point y){ return x.x * y.x + x.y * y.y; }
void dfs(int x){
  for (int ed = head[x]; ed != 0; ed = e[ed].next)
    if (use[e[ed].l] == false){
      use[e[ed].l] = true;
        if (step[e[ed].data] == false){
            step[e[ed].data] = true;
            dfs(e[ed].data);
        }
    }
}
double sqr(double x){
    return x * x;
}
inline bool spfa(int S, int T){
    for(int i = S; i <= T; i++) dis[i] = inf, use[i] = 0;
    dis[T] = 0; use[T] = 1;
    deque <int> q; q.push_back(T);
    while(!q.empty()){
        int now=q.front();q.pop_front();
        for(int k=head[now];k != 0;k = e[k].next) if(e[e[k].re].l > 0 && dis[e[k].data] > dis[now] - e[k].vote){
            dis[e[k].data] = dis[now] - e[k].vote;
            if(!use[e[k].data]){
                use[e[k].data] = true;
                if(!q.empty() && dis[e[k].data] < dis[q.front()])
                    q.push_front(e[k].data);
                    else q.push_back(e[k].data);
            }
        }
        use[now]=0;
    }
    return dis[S] < inf;
}
void bfs(int S, int T){
    for(int i = S; i <= T; i++) step[i] = inf;
    int pl = 1, pr = 1; q[1] = S, step[S] = 0;
    while (pl <= pr && step[T] == inf){
        int x = q[pl++];
        for (int ed = head[x]; ed != 0; ed = e[ed].next)
            if (e[ed].l > 0 && step[e[ed].data] == inf && dis[x] - e[ed].vote == dis[e[ed].data]){
                q[++pr] = e[ed].data;
                step[e[ed].data] = step[x] + 1;
            }
    }
}
inline int dfs(int x, int low, int T){
    if(x == T) return low;
    int used = 0, a;
    for(int ed = now[x]; ed != 0; ed = e[ed].next)
        if(e[ed].l > 0 && dis[x] - e[ed].vote == dis[e[ed].data] && step[x] + 1 == step[e[ed].data]){
            a = dfs(e[ed].data, min(e[ed].l, low - used), T);
            if(a) ans -= a * e[ed].vote, e[ed].l -= a, e[e[ed].re].l += a, used += a;
            if(used == low){
                now[x] = ed;
                return used; 
            }
        }
    now[x] = 0;
    return used;
}
int costflow(int S, int T){
    int flow = 0;
    while(spfa(S, T)){
        for (bfs(S, T); step[T] != inf; bfs(S, T)){
            for (int i = S; i <= T; i++) now[i] = head[i];
            flow += dfs(S, inf, T);
        }
    }
    return flow;
}
int met(Point x, Point y){
    if (x.x * y.x >= 0) return 0;
    double num = (0 - x.x) / (y.x - x.x) * (y.y - x.y) + x.y;
    return num > 0;
}
int main(){
  n = read(), m = read();
  for (int i = 1; i <= n; i++)
    p[i].x = read(), p[i].y = read();
  for (int i = 1; i <= m; i++){
    eg[i].u = read(), eg[i].v = read();
    buildedge(eg[i].u, eg[i].v, i);
    buildedge(eg[i].v, eg[i].u, i);
    deg[eg[i].u]++, deg[eg[i].v]++;
  }
 for (int i = 1; i <= n; i++){
    if (deg[i] % 2 == 1){
      printf("%d\n", -1);
      return 0;
    }
  }
  dfs(1);
  for (int i = 1; i <= m; i++)
    if (use[i] == false){
      printf("%d\n", -1);
      return 0;
    }
  memset(head, 0, sizeof(head)); place = 0;
  memset(deg, 0, sizeof(deg));
  int S = 0, T = n + 1, num = 0;
  for (int i = 1; i <= m; i++){
    if (cross(p[eg[i].u], p[eg[i].v]) < 0) swap(eg[i].u, eg[i].v);
    if (met(p[eg[i].u], p[eg[i].v]) == 1) num = 1; else num = 0;
    ans = ans + num;
    deg[eg[i].u]--, deg[eg[i].v]++;
    build(eg[i].u, eg[i].v, 1, num * 2);
  }
  for (int i = 1; i <= n; i++)
    if (deg[i] > 0)
        build(i, T, deg[i] / 2, 0);
        else build(S, i, -deg[i] / 2, 0);
  costflow(S, T);
  printf("%d\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值