<题目链接>
题目大意:
给定一颗带点权的树,进行两种操作,k=0,更改某一点的点权,k!=0,输出a~b路径之间权值第k大的点的点权。
解题分析:
先通过RMQ的初始化,预处理pre[]数组,并且求出a和b的LCA。然后利用LCA将a、b路径上所有点的点权全部存储起来,将其排序,就可得到a、b路径上权值第k大的点权。具体操作为:利用pre[]数组,从a到LCA的所有点权全部相加,然后再将从b到LCA的所有点权相加,就可以得到a、b路径上所有点的点权。下面用的是LCA转RMQ的方法求解。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int maxn = 8e4+10; struct Edge{ int to,next; }edge[maxn<<1]; int n,q,cnt,num,tot,pre[maxn],val[maxn],fa[maxn]; int st[maxn<<1][20],ver[maxn<<1],dep[maxn<<1],first[maxn],path[maxn]; bool cmp(int a,int b){ return a > b;} int Min(int l,int r){ return dep[l]<dep[r]?l:r; } void addedge(int u,int v){ edge[num].to = v,edge[num].next = pre[u]; pre[u] = num++; } void dfs(int u,int deep){ ver[++cnt] = u; first[u] = cnt; dep[cnt] = deep; for(int i = pre[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(fa[u] == v) continue; fa[v] = u; dfs(v,deep+1); ver[++cnt] = u; dep[cnt] = deep; } } void RMQ_init(){ for(int i = 1; i <= cnt; i++) st[i][0] = i; for(int j = 1; (1 << j) <= cnt; j++) for(int i = 1; i + (1 << j) < cnt; i++) st[i][j] = Min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } int findLCA(int l,int r){ int k = (int)(log(r - l + 1.0) / log(2.0)); return ver[Min(st[l][k],st[r-(1<<k)+1][k])]; } //以上是LCA转RMQ的模板 int main(){ int k,a,b; while(scanf("%d%d",&n,&q)!=EOF){ cnt = num = 0; memset(pre,-1,sizeof(pre)); for(int i = 1; i <= n; i++) scanf("%d",&val[i]); for(int i = 1; i < n; i++){ scanf("%d%d",&a,&b); addedge(a,b); addedge(b,a); } dfs(1,0); RMQ_init(); while(q--){ scanf("%d%d%d",&k,&a,&b); if(k == 0)val[a] = b; else{ int x = first[a], y = first[b]; if(x > y) swap(x,y); int lca = findLCA(x,y); //得到最近公共祖先 tot = 0; while(a != lca){ //将a到最近公共祖先的路劲上所有的点权放入path数组 path[++tot] = val[a]; a = fa[a]; } while(b != lca){ //将a到最近公共祖先的路劲上所有的点权放入path数组 path[++tot] = val[b]; b = fa[b]; } path[++tot] = val[lca]; //再加上lca的点权 if(k > tot) printf("invalid request! "); else{ sort(path+1,path+1+tot,cmp); //将a->b路劲上所有点权排序,然后就可以得到第k大的点权 printf("%d ",path[k]); } } } } return 0; }
2018-10-23