UOJ#3白鸽

Posted shxnb666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UOJ#3白鸽相关的知识,希望对你有一定的参考价值。

题面

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白鸽的主要内容,如果未能解决你的问题,请参考以下文章

UOJ#386UNR#3鸽子固定器(贪心)

UOJ #390. UNR #3百鸽笼

UOJ#50UR #3链式反应(分治FFT,动态规划)

UOJ264 NOIP2016蚯蚓

UOJ 47滑行的窗口

[UOJ282]长度测量鸡