bzoj 5120 [2017国家集训队测试]无限之环——网络流

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5120

旋转的话相当于去掉一个插头、新增一个插头,所以在这两个插头之间连边并带上费用即可。

网格图可以黑白染色,转化为相邻格子间插头的匹配问题。

注意:

1.黑白染色不是移动一格就 fx = ! fx ;每换一行,开头位置的颜色应该和上一行的开头不一样!不然有偶数列的话自己原来写的那个染色就崩了;

2. L 形的判断不是 d&(d>>1) 判断是否有两个相邻的1,如果是第一个位置和最后一个位置是1的话(9)就判断不出来了!可以判断 d != 5 && d != 10 ;

3.对于无解的判断,比较好的是判断插头个数的是不是流量的两倍。

然后就能很慢地 A 了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=8005,K=2005,M=K<<4,INF=N;//<<4:*6 for trans *2 for between
int n,m,t,bh[K][K],hd[N],xnt=1,to[M],nxt[M],cap[M],w[M],bin[5];//=1!
int dis[N],pre[N],info[N],ans,val,flow; bool ins[N];
queue<int> q;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
  while(ch>=‘0‘&&ch<=‘9‘)ret=ret*10+ch-‘0‘,ch=getchar();
  return fx?ret:-ret;
}
int Mn(int a,int b){return a<b?a:b;}
void upd(int &x){x>=4?x-=4:0;}
void add(int x,int y,int z)
{
  if(!x||y==t)val++;//
  to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=1;w[xnt]=z;
  to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-z;
}
void init()
{
  int bin[5];
  bin[0]=1;for(int i=1;i<4;i++)bin[i]=bin[i-1]<<1;
  bool Fx=0,fx=0;
  for(int i=1;i<=n;i++,Fx=!Fx)/////Fx!!!!!
    for(int j=1,fx=Fx;j<=m;j++,fx=!fx)
      {
    int d=rdn(),ct=0;

    if(!fx){for(int k=0;k<4;k++)if(d&bin[k])ct++,add(0,k+bh[i][j],0);}
    else {for(int k=0;k<4;k++)if(d&bin[k])ct++,add(k+bh[i][j],t,0);}
    if(ct==1||ct==3)///ct==1:not a chain but 1,1,2
      {
        int x;//caution for ct=1|3 the diff
        if(ct==1) {for(int k=0;k<4;k++)if(d&bin[k]){x=k;break;}}
        else {for(int k=0;k<4;k++)if(!(d&bin[k])){x=k;break;}}
        int y=x^2,cr=x+bh[i][j];
        for(int k=0;k<4;k++)
          if(k!=x)(fx^(ct==3))?add(k+bh[i][j],cr,1+(k==y)):add(cr,k+bh[i][j],1+(k==y));
      }
    //    else if(ct==2&&(d&(d>>1)))//L
    else if(ct==2&&(d!=5&&d!=10))/////caution 9!!!
      {
        for(int k=0;k<4;k++)
          if(d&bin[k])fx?add((k^2)+bh[i][j],k+bh[i][j],1):add(k+bh[i][j],(k^2)+bh[i][j],1);
      }
    if(!fx)
      {
        if(i>1)add(bh[i][j],2+bh[i-1][j],0);
        if(j<m)add(1+bh[i][j],3+bh[i][j+1],0);
        if(i<n)add(2+bh[i][j],bh[i+1][j],0);
        if(j>1)add(3+bh[i][j],1+bh[i][j-1],0);
      }
      }
}
bool spfa()
{
  memset(dis,0x3f,sizeof dis);dis[0]=0;
  info[0]=INF;info[t]=0; q.push(0);ins[0]=1;
  while(q.size())
    {
      int k=q.front();q.pop();ins[k]=0;
      for(int i=hd[k],v;i;i=nxt[i])
    if(cap[i]&&dis[v=to[i]]>dis[k]+w[i])
      {
        dis[v]=dis[k]+w[i];
        pre[v]=i;info[v]=Mn(info[k],cap[i]);
        if(!ins[v])q.push(v),ins[v]=1;
      }
    }
  return info[t];
}
void ek()
{
  int s=info[t]; flow+=s; ans+=dis[t]*s;//dis[t]!
  for(int i=pre[t];i;i=pre[to[i^1]])
    {cap[i]-=s;cap[i^1]+=s;}
}
int main()
{
  n=rdn();m=rdn();t=1;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++,t+=4)bh[i][j]=t;
  init();  if(val&1){puts("-1");return 0;}//
  while(spfa())ek();  printf("%d\n",flow<<1==val?ans:-1);///
  return 0;
}

还学习了多路增广SPFA算法。大概适用于边权变化范围很小的那种吧。

因为边权相近,所以期望做一次SPFA之后能不仅更新出一条路,还能更多地更新,因为算出来的 dis[ ] 可能对很多条路都适用。

所以就在SPFA之后按照 dis[ ] 来 dfs ,找找所有合法的路径。在这道题上真的能快好多。

注意 dfs 要打 vis 标记,走过的就不再走了。因为网络里可能有环,这个又是按 dis 来走的,所以比如有很多0权边的话,就可能死循环。dinic 没有这个问题是因为它是按 dfn 来走的,dfn 弄出来的一定是 DAG 。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=8005,K=2005,M=K<<4,INF=N;//<<4:*6 for trans *2 for between
int n,m,t,bh[K][K],hd[N],xnt=1,cur[N],to[M],nxt[M],cap[M],w[M],bin[5];//=1!
int dis[N],pre[N],info[N],ans,val,flow; bool ins[N];
queue<int> q;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
  while(ch>=‘0‘&&ch<=‘9‘)ret=ret*10+ch-‘0‘,ch=getchar();
  return fx?ret:-ret;
}
int Mn(int a,int b){return a<b?a:b;}
void upd(int &x){x>=4?x-=4:0;}
void add(int x,int y,int z)
{
  if(!x||y==t)val++;//
  to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=1;w[xnt]=z;
  to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-z;
}
void init()
{
  int bin[5];
  bin[0]=1;for(int i=1;i<4;i++)bin[i]=bin[i-1]<<1;
  bool Fx=0,fx=0;
  for(int i=1;i<=n;i++,Fx=!Fx)/////Fx!!!!!
    for(int j=1,fx=Fx;j<=m;j++,fx=!fx)
      {
    int d=rdn(),ct=0;

    if(!fx){for(int k=0;k<4;k++)if(d&bin[k])ct++,add(0,k+bh[i][j],0);}
    else {for(int k=0;k<4;k++)if(d&bin[k])ct++,add(k+bh[i][j],t,0);}
    if(ct==1||ct==3)///ct==1:not a chain but 1,1,2
      {
        int x;//caution for ct=1|3 the diff
        if(ct==1) {for(int k=0;k<4;k++)if(d&bin[k]){x=k;break;}}
        else {for(int k=0;k<4;k++)if(!(d&bin[k])){x=k;break;}}
        int y=x^2,cr=x+bh[i][j];
        for(int k=0;k<4;k++)
          if(k!=x)(fx^(ct==3))?add(k+bh[i][j],cr,1+(k==y)):add(cr,k+bh[i][j],1+(k==y));
      }
    //    else if(ct==2&&(d&(d>>1)))//L
    else if(ct==2&&(d!=5&&d!=10))/////caution 9!!!
      {
        for(int k=0;k<4;k++)
          if(d&bin[k])fx?add((k^2)+bh[i][j],k+bh[i][j],1):add(k+bh[i][j],(k^2)+bh[i][j],1);
      }
    if(!fx)
      {
        if(i>1)add(bh[i][j],2+bh[i-1][j],0);
        if(j<m)add(1+bh[i][j],3+bh[i][j+1],0);
        if(i<n)add(2+bh[i][j],bh[i+1][j],0);
        if(j>1)add(3+bh[i][j],1+bh[i][j-1],0);
      }
      }
}
bool spfa()
{
  memset(ins,0,sizeof ins);
  memset(dis,0x3f,sizeof dis);dis[0]=0;
  info[0]=INF;info[t]=0; q.push(0);ins[0]=1;
  while(q.size())
    {
      int k=q.front();q.pop();ins[k]=0;
      for(int i=hd[k],v;i;i=nxt[i])
    if(cap[i]&&dis[v=to[i]]>dis[k]+w[i])
      {
        dis[v]=dis[k]+w[i];
        pre[v]=i;info[v]=Mn(info[k],cap[i]);
        if(!ins[v])q.push(v),ins[v]=1;
      }
    }
  return info[t];
}
int dfs(int cr)//for cr!=0 flow must be 1
{
  if(cr==t)return 1; ins[cr]=1;
  for(int& i=cur[cr],v,tmp;i;i=nxt[i])
    if(!ins[v=to[i]]&&cap[i]&&dis[v=to[i]]==dis[cr]+w[i])
      if(tmp=dfs(v)){ans+=w[i];cap[i]--;cap[i^1]++;return 1;}
  return 0;
}
void MCMF()
{
  int tmp;
  while(spfa())
    {
      memcpy(cur,hd,sizeof hd);
      while(tmp=dfs(0))flow+=tmp;
    }
}
int main()
{
  n=rdn();m=rdn();t=1;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++,t+=4)bh[i][j]=t;
  init();  if(val&1){puts("-1");return 0;}//
  MCMF();  printf("%d\n",flow<<1==val?ans:-1);///
  return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/10159227.html

时间: 2024-08-05 18:41:47

bzoj 5120 [2017国家集训队测试]无限之环——网络流的相关文章

BZOJ 2622 [2012国家集训队测试]深入虎穴 SPFA

题意:链接 方法: SPFA 解析: 这题写深搜是作死,别问我为什么. 深搜过程您大概会RE8个点- -! 复杂度类似O(nm)? 其实总体东西就是个SPFA,不过这里面我们需要做一些改动. 这只老虎是DP老虎,他每次会占据你的最优方案. 所以使得你只能用次优方案来更新最优方案以及次优方案. 这就是SPFA的路子. 但是这个路子并不够. 让我们考虑一个点只有一个儿子的时候. 他可能会扫一圈后第一次让他的儿子更新一次答案. 这时候是有最短路而并没次短路的. 但是您肯定会更新一下它的儿子信息. 这时

BZOJ 2039 [2009国家集训队]employ人员雇佣 网络流

链接 BZOJ 2039 题解 这题建图好神,自己瞎搞了半天,最后不得不求教了企鹅学长的博客,,,,发现建图太神了!! s向每个人连sum(e[i][x]) 的边,每个人向T连a[i]的边.两两人之间连2 * e[i][j]的边即可. 最后总的e – maxflow即为答案. 为什么我就没想到"源点向每个人连sum(e[i][x]) 的边"-- 犯的错误: 为了方便,对于双向边,用ADD(u, v, w), ADD(v, u, w)代替了ADD(u, v, w), ADD(v, u,

BZOJ 2038: [2009国家集训队]小Z的袜子

二次联通门 : BZOJ 2038: [2009国家集训队]小Z的袜子 /* BZOJ 2038: [2009国家集训队]小Z的袜子 莫队经典题 但是我并不认为此题适合入门.. Answer = ∑ C (sum[i], 2) / C (r - l + 1, 2) = ∑ (sum[i] ^ 2 - sum[i]) / 2 C (r - l + 1, 2) sum表示区间内数i的出现次数 那么∑sum[i]=r-l+1 2*(r-l+1)! 分母= ----------- = (r-l)*(r-

BZOJ 2038: [2009国家集训队]小Z的袜子(hose)

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 7676  Solved: 3509[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只

BZOJ 2038: [2009国家集训队]小Z的袜子(hose) ( 莫队 )

莫队..先按sqrt(n)分块, 然后按块的顺序对询问排序, 同块就按右端点排序. 然后就按排序后的顺序暴力求解即可. 时间复杂度O(n1.5) ------------------------------------------------------------------------------ #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 50009; int N,

bzoj 2038 [2009国家集训队]小Z的袜子(hose) 莫队算法

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 10239  Solved: 4659[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命--具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两

BZOJ 2039: [2009国家集训队]employ人员雇佣

2039: [2009国家集训队]employ人员雇佣 Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 1369  Solved: 667[Submit][Status][Discuss] Description 作为一个富有经营头脑的富翁,小L决定从本国最优秀的经理中雇佣一些来经营自己的公司.这些经理相互之间合作有一个贡献指数,(我们用Ei,j表示i经理对j经理的了解程度),即当经理i和经理j同时被雇佣时,经理i会对经理j做出贡献,使得所赚得的利

BZOJ 2038: [2009国家集训队]小Z的袜子(hose) 分块

分块大法好 2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MB Submit: 2938  Solved: 1303 [Submit][Status] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只

BZOJ 2038: [2009国家集训队]小Z的袜子(hose)【莫队算法裸题&amp;&amp;学习笔记】

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 9894  Solved: 4561[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两