我可能连普及水平都没有了.这题和[NOI2018]归程我都写炸了倍增LCA,以为主席树+树上差分(Kruskal重构树)写炸调了一晚上,总觉得还可以做些啥来抢救一下
Solution Count on a tree
题目大意:给定一颗树,每个点有一个点权,询问两点简单路径上第(k)小的点权.强制在线
主席树,树上差分
分析:看到第(k)小,我们想到什么?主席树嘛
很多树上的问题我们都可以从它在序列上的做法入手得到启示.在序列上,我们用权值线段树来保存元素,然后用可持久化的方式来保存序列.本质上我们是做了一个前缀和,因为权值线段树满足可加/减性,所以我们可以通过差分的方式来以区间([1,l - 1],[1,r])的权值线段树得到区间([l,r])权值线段树
回到树上.我们从位置(1)开始做前缀和,那树上的"位置(1)"是什么?根节点.
我们可以用保存每一个点到根节点路径上点权的权值线段树的前缀和.同样差分求解.对于两点(u,v)我们如果直接将它们的前缀和加起来是不行的,这样会多算一部分,而多算的那一部分就是它们(LCA)的前缀和以及(LCA)父亲的前缀和
记点(u)到根节点路径点权的权值线段树的前缀和为(d[u]),那么最终我们要求的线段树就是(d[u] + d[v] - d[lca(u,v)] - d[faz[lca(u,v)]]) 可以一次(dfs)求出来
套个树上倍增就好然后我愉快写挂了
注意几点:
- 此题(RE)多半不是你程序有什么大问题,而是(WA)了后由于强制在线(xor)出一个很大的点编号导致(RE)
- 点权要离散化
上蒟蒻的代码:
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1e5 + 100;
int n,m,siz;
vector<int> G[maxn];
inline void addedge(int u,int v){
G[u].push_back(v);
}
namespace ST{//主席树
struct Node{
int ls,rs,val;
}tree[20000000];
int root[maxn],cnt;
inline int newnode(){return ++cnt;}
inline int get(int x){return x == 0 ? 0 : tree[x].val;}
inline void maintain(int root){tree[root].val = get(tree[root].ls) + get(tree[root].rs);}
void add(int pos,int last,int &root,int l = 1,int r = siz){
root = newnode();
if(l == r){
tree[root].val = tree[last].val + 1;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)add(pos,tree[last].ls,tree[root].ls,l,mid),tree[root].rs = tree[last].rs;
else add(pos,tree[last].rs,tree[root].rs,mid + 1,r),tree[root].ls = tree[last].ls;
maintain(root);
}
int query(int k,int add1,int add2,int del1,int del2,int l = 1,int r = siz){//差分
if(l == r)return l;
int mid = (l + r) >> 1,lsiz = get(tree[add1].ls) + get(tree[add2].ls) - get(tree[del1].ls) - get(tree[del2].ls);
if(lsiz >= k)return query(k,tree[add1].ls,tree[add2].ls,tree[del1].ls,tree[del2].ls,l,mid);
else return query(k - lsiz,tree[add1].rs,tree[add2].rs,tree[del1].rs,tree[del2].rs,mid + 1,r);
}
}
const int maxdep = 25;
int val[maxn],tmp[maxn],faz[maxn][maxdep + 1],dep[maxn];
inline int rnk(int x){return lower_bound(tmp + 1,tmp + 1 + siz,x) - tmp;}//离散化
void dfs(int u){//预处理
for(int i = 1;i <= maxdep;i++)//倍增
faz[u][i] = faz[faz[u][i - 1]][i - 1];
ST::add(rnk(val[u]),ST::root[faz[u][0]],ST::root[u]);//由于是到根节点的前缀和,所以上一个版本是它的父亲
for(int v : G[u])
if(v != faz[u][0])
faz[v][0] = u,dep[v] = dep[u] + 1,dfs(v);
}
inline int lca(int x,int y){//屡次写挂的LCA /kk
if(dep[x] > dep[y])swap(x,y);
for(int i = maxdep;i >= 0;i--)
if(dep[faz[y][i]] >= dep[x])
y = faz[y][i];
if(x == y)return x;
for(int i = maxdep;i >= 0;i--)
if(faz[x][i] != faz[y][i])
x = faz[x][i],y = faz[y][i];
return faz[x][0];
}
inline int getans(int u,int v,int k){//查询
int l = lca(u,v);
int pos = ST::query(k,ST::root[u],ST::root[v],ST::root[l],ST::root[faz[l][0]]);
return tmp[pos];
}
int main(){
scanf("%d %d",&n,&m);
for(int i = 1;i <= n;i++)
scanf("%d",&val[i]),tmp[i] = val[i];
for(int a,b,i = 1;i <= n - 1;i++)
scanf("%d %d",&a,&b),addedge(a,b),addedge(b,a);
sort(tmp + 1,tmp + 1 + n),siz = unique(tmp + 1,tmp + 1 + n) - tmp - 1;//离散化
dep[1] = 1;
dfs(1);
for(int lastans = 0,u,v,k,i = 1;i <= m;i++){
scanf("%d %d %d",&u,&v,&k);
printf("%d
",lastans = getans(u ^ lastans,v,k));
}
return 0;
}