13 收心赛2 T3 符文石 题解

符文石

题面

给定一张有 \(n\) 个节点,\(m\) 条有向边边的 DAG ,保证无自环,无重边

每个点有权值 \(a_i\)

对于每个点,求在其连通块内任选两个不同点按位与的最大权值,如果只有一个点,输出-1

\(2 \le n \le 5 \times 10^5, \ 1 \le m \le 10^6, \ 0 \le a_i \le 2^{60}\)

题解

这道题以为是权值线段树,后来写了一下发现不太对,然后改成了暴力,还挂了

正解其实不太难,不过这个上界的证明还是很巧妙的

考虑当前块中能够合成的最大值为 \(M\) ,考虑我们需要维护那些可能合成比 \(M\) 还大的值?

\(a_i <= M\) ,这种情况不需要考虑,因为每个数和其他数与出来一定 \(\le a_i\) ,所以不可能更新答案

\(a_i > M\) ,这是我们需要重点去考虑的,我们设 \(b_i\) 表示第 \(i\) 个数从高到低第一个和 \(M\) 不同的二进制位,并且这一位一定是 \(M\) 为 0,\(a_i\) 为 1,因为如果 \(a_i\) 为 0,\(M\) 为 1,那么 \(a_i < M\) ,不符合我们的前提条件

\(b\) 我们可以得到要留下的 \(a_i\) 只有 \(O(w)\) 个,\(w\) 表示二进制位数

反证法,设 \(i,j\) 为两个集合中的点,若 \(b_i = b_j\) 那么 \(a_i \ \&\ a_j > M\) ,不符合前提条件

所以任意两个数的 \(b_i\) 互不相同,所以符合条件的 \(a_i\) 只有最多 \(O(w)\)

所以对于每个点,我们维护一个这样的集合,以及 \(M\) ,然后按照反图拓扑序合并即可

有个细节,就是可能在合并时出现某个点被合并了多次,从而出错,解决方案是记录每个权值的初始点

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>

using namespace std;

typedef long long ll;

const int N = 5e5 + 10, M = 1e6 + 10;

int n, m;
ll a[N];
int in[N], leaf[N];
int h[N], ne[M], ver[M], tot;

void add (int x, int y) {
    ver[++tot] = y;
    ne[tot] = h[x];
    h[x] = tot;
}

struct node {
    vector <pair <ll, int>> v;
    ll ans;
} t[N];


node Merge (node a, node b) {
    node res;
    res.ans = max (a.ans, b.ans);
    for (auto x : a.v) {
        for (auto y : b.v) {
            if (x.second != y.second) {
                res.ans = max (res.ans, x.first & y.first);
            }
        }
    }
    res.v.resize (a.v.size () + b.v.size ());
    merge (a.v.begin (), a.v.end (), b.v.begin (), b.v.end (), res.v.begin ());
    auto &v = res.v;
    v.erase (unique (v.begin (), v.end ()), v.end ());
    auto it = upper_bound (v.begin (), v.end (), make_pair(res.ans, 1000000));
    v.erase (v.begin (), it);
    return res;
}

void topu () {
    queue <int> q;
    for (int i = 1; i <= n; i++) {
        if (in[i] == 0) {
            q.push (i);
            leaf[i] = 1;
        }
    }

    while (q.size ()) {
        int x = q.front ();
        q.pop ();
        for (int i = h[x]; i; i = ne[i]) {
            int y = ver[i];
            t[y] = Merge (t[y], t[x]);
            if (--in[y] == 0) q.push (y);
        }
    }

}

int main () {

    freopen ("stone.in", "r", stdin);
    freopen ("stone.out", "w", stdout);

    ios :: sync_with_stdio (0);
    cin.tie (0);
    cout.tie (0);

    cin >> n >> m;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        t[i].v.push_back ({a[i], i});
    }
    for (int i = 1; i <= m; i ++) {
        int x, y;
        cin >> x >> y;
        add (y, x);
        in[x] ++;
    }
    topu ();
    for (int i = 1; i <= n; i ++) {
        if (leaf[i]) cout << -1 << ' ';
        else cout << t[i].ans << ' ';
    }

    return 0;
}
posted @ 2025-08-28 14:20  michaele  阅读(29)  评论(0)    收藏  举报