版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/84580575
洛谷传送门
题目描述
假设有 根柱子,现要按下述规则在这 根柱子中依次放入编号为 , , ,…的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何 个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在 根柱子上最多能放多少个球。例如,在 根柱子上最多可放 个球。
对于给定的 ,计算在 根柱子上最多能放多少个球。
输入输出格式
输入格式:
第1 行有 个正整数 ,表示柱子数。
输出格式:
程序运行结束时,将 根柱子上最多能放的球数以及相应的放置方案输出。文件的第一行是球数。接下来的 行,每行是一根柱子上的球的编号。
输入输出样例
输入样例#1:
4
输出样例#1:
11
1 8
2 7 9
3 6 10
4 5 11
说明
感谢 @PhoenixEclipse 提供spj
解题分析
先考虑确定一个 , 我们如何判定需要几个柱子。
很显然类似路径覆盖的思想, 我们从小的数向大的数连边, 那么柱子的数量等于 匹配数。
我们可以动态加点, 跑到柱子数大于 的时候, 每个数, 寻找流量为 的反向边, 然后 到 里正向输出即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <cctype>
#include <vector>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 3600
#define INF 1e8
#define S 0
#define T 3500
#define EPS 1e-8
#define BASE 1700
int n, cnt;
int head[MX], layer[MX];
bool vis[MX];
std::vector <int> vec[MX];
struct Edge {int to, fl, nex;} edge[MX * MX << 1];
template <class C> IN C max(C a, C b) {return a > b ? a : b;}
template <class C> IN C min(C a, C b) {return a < b ? a : b;}
IN void add(R int from, R int to, R int fl)
{
edge[++cnt] = {to, fl, head[from]}, head[from] = cnt;
edge[++cnt] = {from, 0, head[to]}, head[to] = cnt;
}
namespace Dinic
{
std::queue <int> q;
IN bool BFS()
{
std::memset(layer, 0, sizeof(layer));
layer[S] = 1; q.push(S); R int now;
W (!q.empty())
{
now = q.front(); q.pop();
for (R int i = head[now]; ~i; i = edge[i].nex)
{
if (edge[i].fl && (!layer[edge[i].to]))
layer[edge[i].to] = layer[now] + 1, q.push(edge[i].to);
}
}
return layer[T];
}
int DFS(R int now, R int avai)
{
if (now == T) return avai;
R int lef = avai, buf;
for (R int i = head[now]; ~i; i = edge[i].nex)
{
if (edge[i].fl && layer[edge[i].to] == layer[now] + 1)
{
buf = DFS(edge[i].to, min(lef, edge[i].fl));
if (!buf) continue;
lef -= buf, edge[i].fl -= buf, edge[i ^ 1].fl += buf;
if (!lef) return avai;
}
}
return avai - lef;
}
int solve()
{
int ret = 0;
W (BFS()) ret += DFS(S, INF);
return ret;
}
}
void check(R int now, int &id)
{
vis[now] = true;
bool flag = false;
for (R int i = head[now + BASE]; i; i = edge[i].nex)
{
if (i % 2 && edge[i].fl)
{
flag = true;
check(edge[i].to, id);
break;
}
}
if (!flag) return id = now, vec[now].push_back(now), void();
else vec[id].push_back(now);
}
IN bool isok(R int i, R int j)
{return fabs(std::sqrt(i + j) - (int)std::sqrt(i + j)) < EPS;}
int main(void)
{
int foo;
std::memset(head, cnt = -1, sizeof(head));
scanf("%d", &n);
R int lim, tot = 0;
for (lim = 1; ; ++lim)
{
add(S, lim, 1);
add(lim + BASE, T, 1);
for (R int i = 1; i < lim; ++i) if(isok(i, lim)) add(i, lim + BASE, 1);
tot += Dinic::solve();
if (lim - tot > n) {--lim; break;}
}
printf("%d\n", lim);
for (R int i = lim; i; --i)
if (!vis[i]) check(i, foo);
for (R int i = 1; i <= lim; ++i) if (vec[i].size())
{for (R int j = 0; j < vec[i].size(); ++j) printf("%d ", vec[i][j]); puts("");}
}