洛谷 P2765 魔术球问题 题解

作者:岸芷汀兰

一、题目:

洛谷原题

二、思路:

既然是网络流24题中的一道,那么肯定要用到网络流模型。

其实大多数OI题都是模型的运用和转化,此题也不例外。

如果没有做过这道题的同学可以先做一下,再来理解本题可能会容易一些。

我们会发现如果我们这样建图:

\(\forall u < v\)如果\(u + v\)是完全平方数,那么我们就从u向v连一条有向边,那么最终的图G一定是个DAG。

每一根柱子,记最下面的数为\(x\),最上面的数为\(y\),那么这根柱子就对应着图G中的一条从\(x\)\(y\)的路径。

现在把原题改一下,改为给你\(m\),让你求:要想把\(1\sim m\)的数字摆到柱子上,最小需要几个柱子。那是不是就对应着图G的最小路径覆盖问题?

那么现在再来考虑本题,本题是给你\(n\)个柱子,让你求最大的\(m\),那么我们可以依次增加\(m\),检查当前的图G的最小路径覆盖是否小于等于\(n\),如果大于了\(n\),就立即跳出。

至于怎样输出答案,与“最小路径覆盖”那道题的输出方法一模一样,在此不再赘述。

三、代码:

//好看的代码是理解OI题的基础
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

using namespace std;

#define LL long long
#define mem(s, v) memset(s, v, sizeof s)

inline LL read(void) {
    LL x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int inf = 1e9 + 5;
const int maxn = 1e5 + 5;

int to[maxn], nxt[maxn], head[30005], tot = 1;
int cur[maxn];

int n, s, t;
int w[maxn];

inline void add(int x, int y, int z) {
    to[++tot] = y; nxt[tot] = head[x]; head[x] = tot; w[tot] = z;
    to[++tot] = x; nxt[tot] = head[y]; head[y] = tot;
}

inline int id(int x, int y) { return x + y * 10000; }//拆点

inline void AddEdge(int x) {
    for (register int i = 1; i < x; ++i) {
        int tmp = i + x;
        if ((int)sqrt(tmp) * (int)sqrt(tmp) == tmp) {
            add(id(i, 0), id(x, 1), 1);
        }
    }
    add(s, id(x, 0), 1); add(id(x, 1), t, 1);
}//每增加一次m,更新一下图G

int q[maxn], l, r, vis[maxn], h[maxn];

inline bool bfs(void) {
    for (register int i = 1; i <= t; ++i) h[i] = inf, cur[i] = head[i], vis[i] = 0;
    h[s] = 0; l = r = 1; q[r++] = s;
    while (r - l) {
        int u = q[l++]; vis[u] = 0;
        for (register int i = head[u]; i; i = nxt[i]) {
            int v = to[i];
            if (w[i] && h[v] > h[u] + 1) {
                h[v] = h[u] + 1;
                if (!vis[v]) q[r++] = v, vis[v] = 1;
            }
        }
    }
    return h[t] < inf;
}

int dfs(int u, int flow) {
    if (u == t) { return flow; }
    int tmp, used = 0;
    for (register int i = cur[u]; i; i = nxt[i]) {
        int v = to[i]; cur[u] = i;
        if (w[i] && h[v] == h[u] + 1) {
            if (tmp = dfs(v, min(flow - used, w[i]))) {
                w[i] -= tmp; w[i ^ 1] += tmp;
                used += tmp;
                if (used == flow) break;
            }
        }
    }
    return used;
}

int maxflow;

inline void dinic(void) {
    while (bfs()) {
        maxflow += dfs(s, inf);
    }
}

int suc[maxn];

inline void print(int x) {//输出“路径”
    while (x) {
        printf("%d ", x);
        vis[x] = 1; 
        x = suc[x];
    }
}

int main() {
    n = read(); s = id(10000, 1) + 1, t = s + 1;
    int now = 0, cnt = 0;
    while (now <= n) {
        ++cnt; AddEdge(cnt);
        dinic();
        now = max(now, cnt - maxflow);
    }
    printf("%d\n", cnt - 1);
    for (register int i = 1; i <= cnt - 1; ++i) {
        for (register int j = head[i]; j; j = nxt[j]) {
            int v = to[j];
            if (v != s && v != t) {
                if (w[j ^ 1]) {
                    suc[i] = v - 10000;
                    break;
                }
            }
        }
    }
    mem(vis, 0);
    for (register int i = 1; i <= cnt - 1; ++i) {
        if (!vis[i]) print(i), puts("");
    }
    return 0;
}

posted @ 2020-01-08 11:43  蓝田日暖玉生烟  阅读(146)  评论(0编辑  收藏  举报