码迷,mamicode.com
首页 > 其他好文 > 详细

【UOJ#3】白鸽

时间:2019-08-14 09:39:04      阅读:120      评论:0      收藏:0      [点我收藏+]

标签:back   pre   register   ring   line   圆点   vector   为什么   through   

题面

http://uoj.ac/problem/389

题解

当我刚学欧拉回路的时候,刚刚把网络流学完,就知道欧拉回路的题可以用网络流来做,这道题就是啦。

可以说欧拉回路是一类特殊网络流的调整问题(和上下界网络流)差不多。

所以我曾经开过脑洞,有上下界最小流可以用费用流做,但是由于太慢被自己$D$了。

首先,判断存在性直接用欧拉回路的理论去判就行了,

最后已经知道是绕圈圈了,所以我们考虑如何来计算绕行的圈数。

我们把$x$轴正半轴看成圆点的射线,每正着(从上到下)经过一次,就把答案$+1$,反着经过,就把答案$-1$(为什么?因为说明原来算在里面的一圈假了,所以要$-1$)

答案就是原来的费用加上最大费用最大流。

然后再把每条边随意定向,我发现了$2$个问题。

  1. 要是定向之后直接符合了判定,就假了。所以我们直接让编号小的向编号大的联就好了。
  2. 权值有正有负,很麻烦。这样我们直接定向就假设费用都能取到,这样每条边都是负的了,$spfa$就能跑到快一点了(躲开负边就行了)。。。。
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 20500
#define S 0
#define T (n+1)
#define LL long long
#define ri register int
#define INF 1000000007

using namespace std;

int n,m,x[N],y[N];
int p[N],f[N],vis[N];

int getf(int x) {
  if (x==f[x]) return x;
  return f[x]=getf(f[x]);
}

inline int read() {
  int ret=0,f=0; char ch=getchar();
  while (ch<0 || ch>9) f|=(ch==-),ch=getchar();
  while (ch>=0 && ch<=9) ret*=10,ret+=(ch-0),ch=getchar();
  return f?-ret:ret;
}

struct graph {
  vector<int> to,w,c;
  vector<int> ed[N];
  LL dis[N]; int cur[N];
  bool vis[N];
  void add_edge(int a,int b,int aw,int ac) {
    to.push_back(b); w.push_back(aw); c.push_back(ac);  ed[a].push_back(to.size()-1);
    to.push_back(a); w.push_back(0);  c.push_back(-ac); ed[b].push_back(to.size()-1);
  }
  bool spfa() {
    memset(dis,-0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int> q;
    dis[S]=0;q.push(S);vis[S]=1;
    while (!q.empty()) {
      int x=q.front(); q.pop();
      for (ri i=0;i<ed[x].size();i++) {
        int e=ed[x][i];
        if (dis[to[e]]<dis[x]+c[e] && w[e]) {
          dis[to[e]]=dis[x]+c[e];
          if (!vis[to[e]]) vis[to[e]]=1,q.push(to[e]);
        }
      }
      vis[x]=0;
    }
    return dis[T]>-INF;
  }
  int dfs(int x,int lim) {
    if (x==T || !lim) return lim;
    LL sum=0; vis[x]=1;
    for (ri &i=cur[x];i<ed[x].size();i++) {
      int e=ed[x][i];
      if (dis[x]+c[e]==dis[to[e]] && w[e] && !vis[to[e]]) {
        int f=dfs(to[e],min(lim,w[e]));
        w[e]-=f; w[1^e]+=f;
        lim-=f; sum+=f;
        if (!lim) return sum;
      }
    }
    return sum;
  }
  LL zkw() {
    LL ret=0;
    while (spfa()) {
      memset(vis,0,sizeof(vis));
      memset(cur,0,sizeof(cur));
      ret+=dfs(S,INF)*dis[T];
    }
    return ret;
  }
} G;

int main(){
  n=read(); m=read();
  for (ri i=1;i<=n;i++) f[i]=i;
  for (ri i=1;i<=n;i++) x[i]=read(),y[i]=read();
  int ans=0;
  for (ri i=1;i<=m;i++) {
    int u=read(),v=read();
    if (y[u]<0 && y[v]>=0) swap(u,v);
    vis[u]=vis[v]=1;
    f[getf(u)]=getf(v);
    double k=(x[u]==x[v])?-1:(y[v]-y[u]*1.0)/(x[v]*1.0-x[u]);
    if (y[u]!=y[v] && ( x[u]!=x[v] && y[u]/k<x[u] || x[u]==x[v] && x[u]>0) ) {
      if (y[u]>=0 && y[v]<0) ans++,G.add_edge(v,u,1,-2);
      else if (y[u]<0 && y[v]>=0) ans--,G.add_edge(v,u,1,2);
      else G.add_edge(v,u,1,0);
    }
    else G.add_edge(v,u,1,0);
    p[u]--; p[v]++;
  }
  for (ri i=1;i<=n;i++) if (vis[i] && getf(i)!=getf(1)) {
    puts("-1");
    return 0;
  }
  for (ri i=1;i<=n;i++) {
    if (p[i]%2!=0) {
      puts("-1");
      return 0;
    }
    if (p[i]>0) G.add_edge(S,i,p[i]/2,0); else if (p[i]<0) G.add_edge(i,T,-p[i]/2,0);
  }
  if (n==2) {
    puts("0");
    return 0;
  }
  ans+=G.zkw();
  printf("%d\n",ans);
}

 

【UOJ#3】白鸽

标签:back   pre   register   ring   line   圆点   vector   为什么   through   

原文地址:https://www.cnblogs.com/shxnb666/p/11349786.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!