EOJ Monthly 2018.1 F 最小OR路徑
題目鏈接
Description
給定一個有 \(n\) 個點和 \(m\) 條邊的無向圖,其中每一條邊 \(e_i\) 都有一個權值記為 \(w_i\) 。
對於給出的兩個點 \(a\) 和 \(b\) ,求一條 \(a\) 到 \(b\) 的路徑,使得路徑上的邊權的 \(OR\)(位或)和最小,輸出這個值。(也就是說,如果將路徑看做邊的集合 \(\{e_1,e_2,…,e_k\}\),那麽這條路徑的代價為 \(w_1\ OR\ w_2\ OR\ …\ OR\ w_k\),現在求一條路徑使得其代價最小,輸出這個代價。如果不存在這樣的路徑,輸出 \(-1\)。
Input
Easy
\(2\le n \le 10^4, 0 \le m \le 10^6, 0 \le c_i \le 2^{62}-1\)
Hard
\(2\le n \le 10^4, 0 \le m \le 10^6, 0 \le c_i \le 2^{62}-1\)
題解 By zerol
Easy
\(dp[u][k]\) 表示到結點 \(u\) 的代價為 \(k\) 的方案是否存在,然後在圖上轉移(dfs 一下就好了)。
Hard
假設答案的二進制位全是 \(1\),然後從高位到低位考慮,如果將該位置為 \(0\) 不破壞連通性的話就置為 \(0\),這樣肯定最優。
判斷連通性可以用 並查集 或者 搜索,反正 \(O(M)\) 就行。
復雜度 \(O(63M)\) 。
對比
稍微回顧一下之前的 bzoj 2115 [Wc2011] Xor 路徑最大異或和 線性基
共同點:從高位向低位做,判斷當前位能否置為1或0(畢竟都是位運算)
不同點:
Xor那道題是預處理出來一條路徑,以及所有可以補充於其上的路徑。所以,判斷能否置為1即是看能否添加這條路徑。
而這道題,因為OR運算的性質,直觀想來,添加的路徑越少OR和就越小。所以,判斷當前位能否置為0即是通過連通性來判斷。
Code
Easy
#include <bits/stdc++.h> #define maxn 1100 #define maxm 10010 using namespace std; bool vis[maxn][maxn]; struct Edge { int to, ne, w; } edge[maxm << 1]; int tot, ne[maxn]; void add(int u, int v, int w) { edge[tot] = {v, ne[u], w}; ne[u] = tot++; } typedef long long LL; void dfs(int u, int ors) { if (vis[u][ors]) return; vis[u][ors] = true; for (int i = ne[u]; ~i; i = edge[i].ne) { int v = edge[i].to; dfs(v, ors | edge[i].w); } } int main() { memset(ne, -1, sizeof ne); int n, m; scanf("%d%d", &n,&m); int u, v, w; for (int i = 0; i < m; ++i) { scanf("%d%d%d", &u,&v,&w); add(u,v,w); add(v, u, w); } scanf("%d%d", &u, &v); dfs(u, 0); for (int i = 0; i <= 1024; ++i) { if (vis[v][i]) { printf("%d\n", i); return 0; } } puts("-1"); return 0; }
Hard
#include <bits/stdc++.h>
#define maxn 10010
#define maxm 1000010
using namespace std;
typedef long long LL;
vector<int> a[maxn];
struct Edge { int u, v; } edge[maxm];
void add(LL w, int id) {
int cnt = 0;
while (w) {
if (w & 1) a[cnt].push_back(id);
w >>= 1, ++cnt;
}
}
int s, t, n, m, fa[maxn], sz[maxn];
bool exc[maxm], flag[64];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void unionn(int u, int v) {
u = find(u), v = find(v);
if (sz[u] < sz[v]) swap(u, v);
sz[u] += sz[v], fa[v] = u;
}
bool ok(int id) {
memset(exc, 0, sizeof exc);
for (int i = 62; i >= id; --i) {
if (!flag[i]) {
for (auto x : a[i]) exc[x] = true;
}
}
for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1;
for (int i = 0; i < m; ++i) {
if (!exc[i]) unionn(edge[i].u, edge[i].v);
}
return find(s) == find(t);
}
int main() {
scanf("%d%d", &n,&m);
for (int i = 0; i < m; ++i) {
int u, v; LL w;
scanf("%d%d%lld", &u,&v,&w);
edge[i] = {u, v};
add(w, i);
}
scanf("%d%d", &s, &t);
if (!ok(64)) { puts("-1"); return 0; }
LL ans = 0;
for (int i = 62; i >= 0; --i) {
ans <<= 1;
if (!ok(i)) flag[i] = 1, ans |= 1;
}
printf("%lld\n", ans);
return 0;
}
EOJ Monthly 2018.1 F 最小OR路徑