题目
题意
给出一棵 n n n 个节点的数,每个节点有一个权值。之后有 m m m 次询问,每次询问 u , v , k u, v, k u,v,k 表示,树上 u u u 到 k k k 的简单路径上,第 k k k 大的权值是多少
思路
一道图论+数据结构的特别有意思的题目,充分利用了主席树可减的特点。我们查询简单路径 ( u , v ) (u, v) (u,v) 时,可以通过 d f s dfs dfs 序建立主席树。而路径路径上的信息就是 T [ u ] + T [ v ] − T [ l c a ( u , v ) ] − T [ f a [ l c a ( u , v ) ] ] T[u] + T[v] - T[lca(u,v)] - T[fa[lca(u,v)]] T[u]+T[v]−T[lca(u,v)]−T[fa[lca(u,v)]]。需要注意的是, l c a ( u , v ) lca(u,v) lca(u,v) 只减一次,第二次减的是最近公共祖先的父亲节点。由于减了两遍 l c a lca lca 错了两发
代码
const int maxm = 20;
vector<int> G[maxn];
vector<int> v;
int fa[maxn][maxm], dep[maxn];
int vis[maxn], dis[maxn];
int mp[maxn];
struct Node{int ls, rs, sum;}T[maxn*40];
int root[maxn], tot;
int num[maxn], sz, n, m;
int getid(int x){return lower_bound(all(v), x) - v.begin() + 1;}
int build(int l, int r){
int p = ++tot;
T[p].sum = 0;
if(l == r) return p;
int mid = (l + r) >> 1;
T[p].ls = build(l, mid);
T[p].rs = build(mid + 1, r);
return p;
}
void update(int l, int r, int &x, int y, int pos){
x = ++tot;
T[x] = T[y];
T[x].sum++;
if(l == r) return ;
int mid = (l + r) >> 1;
if(mid >= pos) update(l, mid, T[x].ls, T[y].ls, pos);
else update(mid+1, r, T[x].rs, T[y].rs, pos);
}
void dfs(int x){
vis[x] = 1;
rep(i, 1, maxm) fa[x][i] = fa[fa[x][i-1]][i-1];
rep(i, 0, G[x].size()){
if(!vis[G[x][i]]){
dep[G[x][i]] = dep[x] + 1;
dis[G[x][i]] = dis[x] + 1;
fa[G[x][i]][0] = x;
update(1, n, root[G[x][i]], root[x], getid(num[G[x][i]]));
dfs(G[x][i]);
}
}
}
int query(int a, int b){
if(dep[a] > dep[b]) swap(a, b);
int tmp = dep[b] - dep[a];
for(int i=0; (1<<i)<=tmp; i++)
if((1<<i)&tmp) b = fa[b][i];
per(i, 0, maxm)
if(fa[a][i] != fa[b][i])
a = fa[a][i], b = fa[b][i];
int lca = (a==b) ? a : fa[a][0];
return lca;
}
int ask(int l, int r, int x, int y, int z, int faz, int k){
if(l == r) return v[l - 1];
int mid = (l + r) >> 1;
int sum = T[T[x].ls].sum + T[T[y].ls].sum - T[T[z].ls].sum - T[T[faz].ls].sum;
if(sum >= k) return ask(l, mid, T[x].ls, T[y].ls, T[z].ls, T[faz].ls, k);
else return ask(mid+1, r, T[x].rs, T[y].rs, T[z].rs, T[faz].rs, k - sum);
}
int main()
{
sdd(n, m);
rep(i, 1, n+1) sd(num[i]), v.pb(num[i]);
sort(all(v));
v.erase(unique(all(v)), v.end());
rep(i, 1, n){
int u, v; sdd(u, v);
G[u].pb(v); G[v].pb(u);
}
root[0] = build(1, n); fa[1][0] = 0;
update(1, n, root[1], root[0], getid(num[1]));
dfs(1);
rep(i, 0, m){
int u, v, k; sddd(u, v, k);
int w = query(u, v);
// cout<<u<<" "<<v<<" ** "<<w<<endl;
pd(ask(1, n, root[u], root[v], root[w], root[fa[w][0]], k));
}
return 0;
}