题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3078
题目大意:定点修改。查询树中任意一条树链上,第K大值。
解题思路:
先用离线Tarjan把每个Query树链的LCA求出来。
LCA中对连接树Dfs的时候,令p[v]=u,记录v的前驱。
LCA结束后,对于每个Query:
从u开始回溯到LCA,记录值。从v开始回溯到LCA,记录值。
再加上LCA这个点的值,形成一条完整树链。特判树链长度是否小于K。
对树链中的值,从大到小排序,取第K大即可。
#include "cstdio" #include "cstring" #include "vector" #include "algorithm" using namespace std; #define maxn 80005 int head[maxn],qhead[maxn],lag[maxn],kth[maxn],tot1,tot2,f[maxn],vis[maxn],ancestor[maxn],p[maxn]; bool cmp(int a,int b) {return a>b;} struct Edge { int to,next; }e[maxn*2]; struct Query { int from,to,next,idx; }q[maxn*2]; void addedge(int u,int v) { e[tot1].to=v; e[tot1].next=head[u]; head[u]=tot1++; } void addquery(int u,int v,int idx) { q[tot2].from=u; q[tot2].to=v; q[tot2].next=qhead[u]; q[tot2].idx=idx; qhead[u]=tot2++; } int find(int x) {return x!=f[x]?f[x]=find(f[x]):x;} void Union(int u,int v) { u=find(u),v=find(v); if(u!=v) f[v]=u; } void LCA(int u) { vis[u]=true; f[u]=u; for(int i=head[u];i!=-1;i=e[i].next) { int v=e[i].to; if(!vis[v]) { p[v]=u; LCA(v); Union(u,v); } } for(int i=qhead[u];i!=-1;i=q[i].next) { int v=q[i].to; if(vis[v]) ancestor[q[i].idx]=find(v); //or storage e[i].lca=e[i^1].lca=find(v) } } int main() { //freopen("in.txt","r",stdin); int T,n,m,u,v,c,cmd; scanf("%d%d",&n,&m); tot1=tot2=0; memset(head,-1,sizeof(head)); memset(qhead,-1,sizeof(qhead)); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) scanf("%d",&lag[i]); for(int i=0; i<n-1; i++) { scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } for(int i=0; i<m; i++) { scanf("%d%d%d",&cmd,&u,&v); if(cmd==0) lag[u]=v; else { addquery(u,v,i); addquery(v,u,i); kth[i]=cmd; } } LCA(1); for(int i=0; i<tot2; i=i+2) { int u=q[i].from,v=q[i].to,idx=q[i].idx; int ed=ancestor[idx]; vector<int> chain; while(u!=ed) chain.push_back(lag[u]),u=p[u]; while(v!=ed) chain.push_back(lag[v]),v=p[v]; chain.push_back(lag[ed]); if(chain.size()<kth[idx]) {printf("invalid request!\n");continue;} else { sort(chain.begin(),chain.end(),cmp); printf("%d\n",chain[kth[idx]-1]); } } }