D - Points Construction Problem

D 构造

题意


有一个无限大的白色网格,要求你涂黑 n 个点,构造 m 个异色点对,求这个点序列。

思路


我们将黑点想象成黑色正方形,异色点对数想象成边长。那么这个图可以看成由多个 ”黑色正方形“ 构成的黑色多边形组成。

先考虑上界:由于一个黑色正方形最多有 4 条边,因此 m >= 4n 时无解。留意到,黑色多边形的边长一定是偶数,因此奇数时无解。

再考虑下界:一个 a x b 黑色矩形,最多提供 2 (a + b) 个异色点对。挖掉它的边边角角,由小学数学可知,边长不变。有了这个性质,就很方便了。

我们可以将 n 个点,构造成 a x b 的黑色矩形,然后挖掉它的边边角角,就可以得到 n 个点时,最小异色点对(边长最小)的点序列,记现在矩形的边长为 minM。

然后我们来扩展它的边长:
1、找最右上方的黑色正方形, 记录它邻接的黑色正方形数 p
2、将这个黑色正方形放到无穷远处
3、minM += 2 * p (边长增加那么多)
4、如果当前边长大于 m,则停止扩展,否则继续执行1

当扩展完成后,当前边长有可能比目标边长 m 多 2,因此,我们随便选一个无穷远的黑色正方形,将它放到恰与一个黑色正方形邻接的地方就可以了。

时间复杂度 O(n)

代码


#include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
const int DX[] = {-1, 0, 1, 0};
const int DY[] = {0, 1, 0, -1};
set<pii, greater<pii>> ans;

int deg(pii p) {
    int cur = 0;
    for (int i = 0; i < 4; ++i) 
        cur += ans.count(pii{p.first + DX[i], p.second + DY[i]});
    return cur;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int t, n, m;
    cin >> t;
    while (t--) {
        ans.clear();
        cin >> n >> m;

        if (m > 4 * n || m % 2) {
            cout << "No\n";
            continue;
        }

        int minM = -1, minA, minB;
        for (int i = 1; i <= n; ++i) {
            int j = (n + i - 1) / i;
            if (minM == -1 || 2 * (i + j) < minM) {
                minA = i;
                minB = j;
                minM = 2 * (i + j);
            }
        }

        if (m < minM) {
            cout << "No\n";
            continue;
        }

        for (int i = 1; i <= minA && ans.size() != n; ++i)
            for (int j = 1; j <= minB && ans.size() != n; ++j)
                ans.emplace(i, j);

        int gen = 0;
        while (minM < m) {
            auto p = *ans.begin();
            ans.erase(p);

            minM += deg(p) * 2;

            p.first = p.second = --gen;
            ans.emplace(p);
        }

        if (minM != m) {
            auto p = *ans.rbegin();
            ans.erase(p);

            auto q = *ans.rbegin();

            for (int i = 0; i < 4; ++i) {
                pii p = {q.first + DX[i], q.second + DY[i]};
                if (deg(p) == 1) {
                    ans.emplace(p);
                    break;
                }
            }
        }

        cout << "Yes\n";
        for (auto p: ans) {
            cout << p.first << ' ' << p.second << "\n";
        }
    }
    return 0;
}
全部评论

相关推荐

点赞 评论 收藏
分享
在秋招的香菇很中二:把实践经历、校园经历删了,把课设包装成项目经历写上去。
点赞 评论 收藏
分享
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务