[CF1479D] Odd Mineral Resource
[题目链接]
http://codeforces.com/contest/1479/problem/D
[题解]
做法 \(1\) :
考虑树上莫队 , 维护每种颜色的 \(cnt\) 数组 , 直接用树状数组 / 线段树这样的带 \(log\) 数据结构会得到一个复杂度为 \(O(N \sqrt{N} log(N))\) 的做法。 这样显然是不优的 , 考虑均摊 , 将线段树改为分块即可。这样的复杂度为 \(O(N\sqrt{N})\)
做法 \(2\) :
不妨给每种颜色分配一个 \(2 ^ {64}\) 以内的随机权值。 记 \(f(u , l , r)\) 表示从根节点到节点 \(u\) 路径上颜色种类在 \([l , r]\) 区间内的点得权值异或和。 这可以用可持久化线段树轻松维护。 那么
条路径的权值异或和 \(f(u , v , l , r)\) 就可以通过四棵线段树异或出。
考虑二分求解 , 若 \(f(u , v , l , r) = 0\) , 则退出。 否则在左子树 / 右子树中分别寻找答案。
这样对于一组询问的正确率为 \(1 - 2 ^ {-64}\)。
那么 \(q\) 组询问的正确率至少是 :
\(\begin{aligned} \Pr\left[ \bigwedge_{i=1}^{q} A_i \right] & = 1 - \Pr\left[ \bigvee_{i=1}^q \bar A_i \right] \\ & \geq 1 - \sum_{i=1}^q \Pr[\bar A_i] \\ & = 1 - \sum_{i=1}^q \left( 1 - \Pr[A_i] \right) \\ & \geq 1 - \sum_{i=1}^q \left( 1 - \left( 1 - 2^{-64} \right) \right) \\ & = 1 - 2^{-64}q, \end{aligned}\)
故正确率大于 \(1 - 2 ^ {44}\)。 是足以通过这道题的。
时间复杂度 : \(O(NlogN)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 3e5 + 5 , MM = MN * 40;
typedef unsigned long long ull;
mt19937_64 get_value(355);
int N , M , fa[MN] , size , A[MN] , ans;
ull key[MM] , sum[MM];
int lc[MM] , rc[MM] , root[MM];
inline int query(int a , int b , int c , int d , int l , int r , int ql , int qr) {
if ((sum[a] ^ sum[b] ^ sum[c] ^ sum[d]) == 0) return -1;
if (l == r) return l;
int mid = l + r >> 1; int res = -1;
if (mid >= ql) {
res = query(lc[a] , lc[b] , lc[c] , lc[d] , l , mid , ql , qr);
if (res != -1) return res;
}
if (qr > mid) {
res = query(rc[a] , rc[b] , rc[c] , rc[d] , mid + 1 , r , ql , qr);
if (res != -1) return res;
}
return -1;
}
inline void add(int &now , int old , int l , int r , int x , ull val) {
now = ++size; lc[now] = lc[old] , rc[now] = rc[old];
sum[now] = sum[old] ^ val;
if (l == r) return;
int mid = l + r >> 1;
if (mid >= x) add(lc[now] , lc[old] , l , mid , x , val);
else add(rc[now] , rc[old] , mid + 1 , r , x , val);
}
struct Edge {
int to , nxt;
} E[MN << 1];
int tot , head[MN];
inline void AddEdge(int u , int v) {
E[++tot] = (Edge) {v , head[u]};
head[u] = tot;
}
int heavy[MN] , siz[MN] , dep[MN] , top[MN];
inline void dfs1(int x) {
add(root[x] , root[fa[x]] , 1 , N , A[x] , key[A[x]]);
siz[x] = 1;
for (int i = head[x]; i; i = E[i].nxt) {
int v = E[i].to; if (v == fa[x]) continue;
dep[v] = dep[x] + 1 , fa[v] = x , dfs1(v) , siz[x] += siz[v];
if (siz[v] > siz[heavy[x]]) heavy[x] = v;
}
}
inline void dfs2(int x) {
if (heavy[x]) top[heavy[x]] = top[x] , dfs2(heavy[x]);
for (int i = head[x]; i; i = E[i].nxt) {
int v = E[i].to; if (v == fa[x] || v == heavy[x]) continue;
top[v] = v , dfs2(v);
}
}
inline int lca(int u , int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u , v);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u , v);
return v;
}
int main() {
scanf("%d%d" , &N , &M);
for (int i = 1; i <= N; ++i) scanf("%d" , &A[i]) , key[i] = get_value();
for (int i = 1; i < N; ++i) {
int u , v; scanf("%d%d" , &u , &v);
AddEdge(u , v) , AddEdge(v , u);
}
dfs1(1); top[1] = 1; dfs2(1);
while (M--) {
int u , v , sl , sr; scanf("%d%d%d%d" , &u , &v , &sl , &sr);
int LCA = lca(u , v);
printf("%d\n" , query(root[u] , root[v] , root[LCA] , root[fa[LCA]] , 1 , N , sl , sr));
}
return 0;
}