2+ doors(贪心,构造)

127 阅读2分钟

Problem - D - Codeforces

题目描述

要求构造一个长度为 nn 的数组 aa,首先满足以下 qq 个条件。其中每个条件包含三个数 i,j,xi,j,x,表示在数组中 aiaj=xa_i | a_j = x。在满足以上条件的前提下,输出字典序最小的数组,题目保证这样的答案存在。

输入样例

4 3
1 2 3
1 3 2
4 1 2

输出样例

0 3 2 2 

题目分析

这是一道基于 按位贪心 的构造问题。

首先我们对于最终数组的每一位进行分析,即在 aiaj=x(ji)a_i|a_j=x(j\ge i) 的第 kk 位中,若两者的或值为 00,则意味着 aia_iaja_j 的第 kk 位必为 00,我们先按照这个条件对相应的每一个数增加限制;若或值为 11,则意味着两数的第 kk 位至少有一个 11,我们按照贪心的思想,要保证数组的字典序最小,则应该优先给 aja_j 的第 kk 位与 11。因此我们统计完所有的 qq 次询问对 aia_i 的第 kk 位的所有 aja_j 的约束,按所有与 aia_i 相关的 aja_j 的第 kk 位是否在第一个条件下强制为 00 来进行最后的判断。

注意一定要按位进行分析,而不能直接按询问的先后进行分析,通过构造样例 3  2  2  3  1  1  2  13\;2\;2\;3\;1\;1\;2\;1,得到答案 0  1  10\;1\;1 而最优解为 0  1  00\;1\;0 可知这样得到的答案并非最优。

Accept代码

#include <bits/stdc++.h>

using namespace std;

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

    int n, q;
    cin >> n >> q;
    vector<int> x(q), y(q), f(q), res(n + 1);
    for (int i = 0; i < q; i ++)
    {
        cin >> x[i] >> y[i] >> f[i];
        if (x[i] > y[i]) swap(x[i], y[i]);
    }
    
    for (int i = 0; i < 30; i ++)
    {
        vector<vector<int>> g(n + 1);
        vector<int> h(n + 1);
        for (int j = 0; j < q; j ++)
        {
            if ((f[j] >> i) % 2 == 0) h[x[j]] = h[y[j]] = 1;
            else g[x[j]].push_back(y[j]);
        }
        for (int j = 1; j <= n; j ++)
        {
            if ((res[j] >> i) % 2 || g[j].empty()) continue;
            bool flg = true;
            for (int k : g[j])
                if (k == j || h[k])
                {
                    res[j] |= 1 << i;
                    flg = false;
                    break;
                }
            if (flg) for (int k : g[j])
                res[k] |= 1 << i;
        }
    }

    for (int i = 1; i <= n; i ++) cout << res[i] << ' ';
    return 0;
}