题目描述:这里

这道题是网络流问题中第一个难点,也是一个很重要的问题

如果直接建图感觉无从下手,因为如果不知道放几个球我就无法得知该如何建图(这是很显然的,比如我知道 $1+48=49=7^2$ ,可是我都不知道是否能放到第48个球,那我怎么知道如何建边呢?)

所以这时就体现出了一个很重要的想法:枚举答案!!!

我们知道,正常有二分答案的做法,可以二分一个答案然后检验

这里用类似的想法,但由于答案比较小而且建图更方便,所以我们直接从小往大枚举答案即可

之所以建图更方便,是因为如果我们从小向大枚举答案,那么原先建好的边是不用动的,因为原先的球一定要放,所以我们只需研究新来的球就可以了

而且还有一个好处,就是这样的话我们只需在残余网络上跑最大流,所以速度会更快一些?(口胡)

这里还有一个问题:如何建图?

直接从源点向新来的球连边,然后由新来的球向汇点连边,容量为1?

那这最大流不是要多少有多少的吗......

所以这样做显然不正确

正确的做法:把一个球拆成两个(设为x与y),然后由源点向x连边,容量为1,由y向汇点连边,容量为1,

接下来,我们找出所有满足与当前球编号和为完全平方数的球,将当前球的x点向那些球的y点连边,容量为1

然后每次在残余网络上跑最大流,如果最大流为0则说明需要新加一个柱子,加到柱子数超过给出的即结束

稍微解释一下这种做法的原因:我们把球拆成两个点以后,可以看做是一个球的上下两面,x表示与原先有的球相接,y表示在它上面放新的球的能力。

这样的话,我们把新来的x与原先合法的y相连后跑最大流,如果新的最大流不为0,说明这个新来的球能成功地放在一个原有的球上(这是因为最大流不为0说明了一条原来由某个y指向汇点的边本来流量是0,但现在流量变成了1而且保证了原有流量不变,这也就说明新来的球放在了一个柱上)

那么就自然是合法的了

然后是下一个问题(所以说这道题是好题,因为有很多个问题)

如何输出方案?

基于上面的解释,输出方案就变得简单了:我们从小往大枚举每个球,如果这个球还没被放在一个柱上则新开一个柱,然后顺着这个球的y点向下寻找,每找到一个没放下的点就放在这个柱上即可

可能说的有些抽象,具体解释一下:

我们从y点向外找边,是因为从y出发的边只要终点不是汇点那么一定是反向边!

如果反向边容量不为0,说明对应的正向边有1的流量,也就说明这个点之上被放上了一个点!

那么我们只需找出这个点,然后向下递归即可

注意找到一个点即可结束本层的寻找,因为一个球上最多只能放一个球啊

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
const int inf=0x3f3f3f3f;
struct Edge
{
int next;
int to;
int val;
}edge[2000005];
int head[10005];
bool used[10005];
int cur[10005];
int cnt=1;
int st,ed;
int n;
int dis[10005];
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
void add(int l,int r,int w)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
edge[cnt].val=w;
head[l]=cnt++;
}
int ide(int x)
{
return (x&1)?x+1:x-1;
}
bool bfs()
{
memcpy(cur,head,sizeof(head));
memset(dis,0,sizeof(dis));
queue <int> M;
M.push(st);
dis[st]=1;
while(!M.empty())
{
int u=M.front();
M.pop();
for(int i=head[u];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(edge[i].val&&!dis[to])dis[to]=dis[u]+1,M.push(to);
}
}
return dis[ed];
}
int dfs(int x,int lim)
{
if(x==ed)return lim;
int ret=0;
for(int i=cur[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(edge[i].val&&dis[to]==dis[x]+1)
{
int temp=dfs(to,min(lim,edge[i].val));
if(temp)
{
lim-=temp;
ret+=temp;
edge[i].val-=temp;
edge[ide(i)].val+=temp;
if(!lim)break;
}
}
cur[x]=i;
}
return ret;
}
int dinic()
{
int ret=0;
while(bfs())ret+=dfs(st,inf);
return ret;
}
void print(int x)
{
used[x]=1;
printf("%d ",x);
for(int i=head[(x<<1)|1];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(!edge[i].val||used[to>>1]||to==ed)continue;
print(to>>1);
break;
}
}
int main()
{
scanf("%d",&n);
init();
int tot;
st=0,ed=1;
int s=0;
for(tot=1;tot;tot++)
{
add(st,tot<<1,1);
add(tot<<1,st,0);
add((tot<<1)|1,ed,1);
add(ed,(tot<<1)|1,0);
for(int j=1;j*j<2*tot;j++)
{
if(j*j>tot)add(tot<<1,((j*j-tot)<<1)|1,1),add(((j*j-tot)<<1)|1,tot<<1,0);
}
if(!dinic())s++;
if(s>n)break;
}
printf("%d\n",tot-1);
for(int i=1;i<tot;i++)
{
if(used[i])continue;
print(i);
printf("\n");
}
return 0;
}

  

网络流24题——魔术球问题 luogu 2765的更多相关文章

  1. COGS396. [网络流24题]魔术球问题(简化版

    问题描述: 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,4......的球. (1)每次只能在某根柱子的最上面放球. (2)在同一根柱子中,任何2个相邻球的编号之和为完全平 ...

  2. [luogu2765 网络流24题] 魔术球问题 (dinic最大流)

    传送门 题目描述 «问题描述: 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球. (1)每次只能在某根柱子的最上面放球. (2)在同一根柱子中,任何2个相邻球的编号之 ...

  3. Libre 6009 「网络流 24 题」软件补丁 / Luogu 2761 软件安装问题 (最短路径,位运算)

    Libre 6009 「网络流 24 题」软件补丁 / Luogu 2761 软件安装问题 (最短路径,位运算) Description T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放 ...

  4. Libre 6006 「网络流 24 题」试题库 / Luogu 2763 试题库问题 (网络流,最大流)

    Libre 6006 「网络流 24 题」试题库 / Luogu 2763 试题库问题 (网络流,最大流) Description 问题描述: 假设一个试题库中有n道试题.每道试题都标明了所属类别.同 ...

  5. 网络流24题——试题库问题 luogu 2763

    题目描述看:这里 这是我们遇到的第一个要求输出方案的问题 考虑建图然后用最大流思想: 首先由源点向每一道试题连边,容量为1 然后由每一种试题类型向汇点连边,容量为需求量 最后由每一道试题向可能属于的试 ...

  6. 网络流24题——骑士共存问题 luogu 3355

    题目描述:这里 从这里开始,我们涉及到了一个新的问题:最小割问题 首先给出一些定义(本人根据定义自己口胡的): 一个流网络中的一个割是一个边集,使得割掉这些边集后源点与汇点不连通 而最小割问题就是一个 ...

  7. 网络流24题——数字梯形问题 luogu 4013

    题目描述:这里 极其裸的一道费用流问题 首先分析第一问,由于要求一个点只能经过一次,所以我们将梯形中的每一个点拆成两个点(记为入点和出点,顾名思义,入点用来承接上一行向这一行的边,出点用来向下一行连边 ...

  8. LOJ6003 - 「网络流 24 题」魔术球

    原题链接 Description 假设有根柱子,现要按下述规则在这根柱子中依次放入编号为的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法 ...

  9. LibreOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖

    6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...

随机推荐

  1. git 学习(4) ----- git rebase

    使用git rebase 的前提是多人协作下的分支开发,如果是单人开发,那就没有必要使用它了,这是由git rebase 的作用所决定的,git rebase 有两大作用:一个是与主分支保持同步,一个 ...

  2. Retrofit原理

    Retrofit原理解析最简洁的思路 Retrofit 工作原理总结 从架构角度看Retrofit的作用.原理和启示 Retrofit主要是在create方法中采用动态代理模式实现接口方法:这个过程构 ...

  3. word 2013 标题设置多级列表

    1.问题 要设置标题为多级列表,批量应用 2.解决 1选标题1 2选标题2 ...以此类推.点确定保存即可

  4. Linux lvs-DR模式配置详解

    本篇文档主要是记录DR模式实现过程,以及各配置步骤的原理.“lvs三种模式工作原理”中描述了LVS的NAT.DR.TUN三种模式的工作原理. DR模式是通过director将报文源和目标MAC地址修改 ...

  5. 题解:[JSOI2004]平衡点 / 吊打XXX

    这个题目算是一个模拟退火的板子题 物重一定,绳子越短,重物越低,势能越小,势能又与物重成正比 使得$\sum_{i=1}^nd[i]*w[i]$也就是总的重力势能最小,可以使得系统平衡 交了两面半.. ...

  6. Nginx-反向代理实现

    Nginx 反向代理操作案例 Nginx反向代理的组件模块 upstream模块介绍->点我< http_proxy_module模块介绍->点我< 环境准备 1)四台服务器都 ...

  7. Java 8 特性 —— Stream

    Stream 是用函数式编程方式在集合类上进行复杂操作的工具,其集成了Java 8 中的众多新特性之一的聚合操作,开发者可以更容易地使用Lambda表达式,并且更方便地实现对集合的查找.遍历.过滤以及 ...

  8. Java IO系列之一:IO

    1. 概述 Java IO一般包含两个部分: 1.java.io包中堵塞型IO: 2.java.nio包中的非堵塞型IO,通常称为New IO. java.io包下,分为四大块近80个类: 1.基于字 ...

  9. python放弃篇(Django/爬虫)

    第一篇:Django系列 第二篇:爬虫系列 待续……

  10. showdoc 文档管理系统

    ==========================showdoc 简介==========================在线文档管理系统很多, 比如阿里的语雀.腾讯的 TAPD 平台也包括文档管理 ...