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;
}

浙公网安备 33010602011771号