[Luogu P2765] [網路流24題] 魔術球問題
阿新 • • 發佈:2018-11-28
洛谷傳送門
題目描述
假設有 根柱子,現要按下述規則在這 根柱子中依次放入編號為 , , ,…的球。
(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("");}
}