题目描述:这里

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

如果直接建图感觉无从下手,因为如果不知道放几个球我就无法得知该如何建图(这是很显然的,比如我知道 $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 工具的话可以使用GitHub桌面端管理git、码云上的代码

    git版本控制  廖雪峰老师的git教程 git是linus 1991年创建了开源的linux...已成为最大的服务器系统软件 集中式的版本控制器:CVS.SVN.ClearCase是IBM的收费软件 ...

  2. Comet OJ - Contest #1

    A:随便怎么暴力. #include<bits/stdc++.h> using namespace std; #define ll long long #define N 25 char ...

  3. Linux keepalived+nginx实现主从模式

    双机高可用方法目前分为两种: 主从模式:一台主服务器和一台从服务器,当配置了虚拟vip的主服务器发送故障时,从服务器将自动接管虚拟ip,服务将不会中断.但主服务器不出现故障的时候,从服务器永远处于浪费 ...

  4. git常用命令介绍

    一,仓库 1. git init    把当前目录变成git可管理得仓库,也就是初始化仓库 2. git add 文件名            在仓库中添加新文件 3. git commit -m & ...

  5. 常用API接口签名验证参考

    项目中常用的API接口签名验证方法: 1. 给app分配对应的key.secret2. Sign签名,调用API 时需要对请求参数进行签名验证,签名方式如下: a. 按照请求参数名称将所有请求参数按照 ...

  6. functools模块中partial的使用

    一.简介 functools.partial(func,* args,**关键字) 返回一个新的部分对象,当被调用时,其行为类似于使用位置参数args 和关键字参数关键字调用的func.如果为调用提供 ...

  7. Educational Codeforces Round 63 (Rated for Div. 2) D. Beautiful Array(动态规划.递推)

    传送门 题意: 给你一个包含 n 个元素的序列 a[]: 定义序列 a[] 的 beauty 为序列 a[] 的连续区间的加和最大值,如果全为负数,则 beauty = 0: 例如: a[] = {1 ...

  8. Linux基础知识梳理

    Linux基础知识梳理 Linux内核最初只是由芬兰人林纳斯?托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的.Linux是一套免费使用和自由传播的类Unix操作系统,是 ...

  9. kubernetes之管理容器的计算资源

    资源类型 CPU 和 memory 都是 资源类型.资源类型具有基本单位.CPU 的单位是 core,memory 的单位是 byte.这些都统称为计算资源. CPU含义: CPU 资源的限制和请求以 ...

  10. 半导体知识:蚀刻(Etch)工艺讲解

    本文转载自微信公众号 - 半导体行业观察  , https://mp.weixin.qq.com/s/F3LXiub6n4iYsQDqDH9K_g