题目分析
首先我们发现,这个题目长得有点像带修改区间第k大,只不过被放在了树上。那么就考虑树状数组套主席树的经典解法吧。
我们设要求x到y的路径上的第k大,o为x和y的lca,那么我们可以利用前缀和,前缀和(x)+前缀和(y)-前缀和(o)-前缀和(o的父亲)就是我们要求的。
现在看修改,修改了x之后,以x为根的子树都会被修改的。这让我们想到了dfs序,可以在入序处将该权值数量+1,出序+1处将该权值-1,即可快速维护前缀和。
由以上零散的思路,我们可以得到一个算法:
1.离线读入所有操作,哈希涉及到的节点值
2.遍历子树,预处理倍增,并求出dfs序
3.用树状数组套主席树维护
4.对于每一个节点,我们在其dfs入序处将该权值数量+1,在其dfs出序+1处将该权值数量-1
5.对于修改操作,我们消除上一个权值的影响,添加这一个权值的影响
6.对于询问操作,我们将与x和y有关的查询值保存一份,再将与o和o的父亲有关的查询值保存一份,进行查询。(这句话没讲明白,其实就是和普通的树状数组套主席树的查询差不多)
注意事项
1.请注意要求的是第k大值
2.请注意开大主席树空间
3.请注意开大哈希数组空间
总之因为以上三点,本蒟蒻的代码调了很久很久,并渐进等于神犇的代码了…
代码
#include<bits/stdc++.h>
using namespace std;
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int N=80005,M=30005;
int n,q,js,tt=1,tot,now,cur;
int h[N],to[N<<1],ne[N<<1],rt[N],Q[2][50];
int a[N],b[N<<1],K[M],A[M],B[M],in[N],le[N],pre[N][18],dep[N];
struct node{int ls,rs,sum;}tr[N*120];
void adde(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
int getn(int x) {return lower_bound(b+1,b+1+tt,x)-b;}
int lowbit(int x) {return x&(-x);}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=17;i>=0;--i) if(dep[pre[x][i]]>=dep[y]) x=pre[x][i];
if(x==y) return x;
for(int i=17;i>=0;--i)
if(pre[x][i]!=pre[y][i]) x=pre[x][i],y=pre[y][i];
return pre[x][0];
}
int ask(int l,int r,int kth) {
if(l==r) return l;
int s0=0,s1=0,mid=(l+r)>>1;
for(int i=1;i<=Q[0][0];++i) s0+=tr[tr[Q[0][i]].ls].sum;
for(int i=1;i<=Q[1][0];++i) s1+=tr[tr[Q[1][i]].ls].sum;
if(s0-s1>=kth) {
for(int i=1;i<=Q[0][0];++i) Q[0][i]=tr[Q[0][i]].ls;
for(int i=1;i<=Q[1][0];++i) Q[1][i]=tr[Q[1][i]].ls;
return ask(l,mid,kth);
}
else {
for(int i=1;i<=Q[0][0];++i) Q[0][i]=tr[Q[0][i]].rs;
for(int i=1;i<=Q[1][0];++i) Q[1][i]=tr[Q[1][i]].rs;
return ask(mid+1,r,kth-(s0-s1));
}
}
int query(int x,int y,int kth) {
int o=lca(x,y),js=dep[x]+dep[y]-dep[o]*2+1;
if(js<kth) return -1;
Q[0][0]=Q[1][0]=0;//0:add 1:sub
for(int i=in[x];i;i-=lowbit(i)) Q[0][++Q[0][0]]=rt[i];
for(int i=in[y];i;i-=lowbit(i)) Q[0][++Q[0][0]]=rt[i];
for(int i=in[o];i;i-=lowbit(i)) Q[1][++Q[1][0]]=rt[i];
for(int i=in[pre[o][0]];i;i-=lowbit(i)) Q[1][++Q[1][0]]=rt[i];
return b[ask(1,tt,js-kth+1)];
}
void ins(int l,int r,int &x,int num,int w) {
if(!x) x=++cur; tr[x].sum+=w;
if(l==r) return;
int mid=(l+r)>>1;
if(num<=mid) ins(l,mid,tr[x].ls,num,w);
else ins(mid+1,r,tr[x].rs,num,w);
}
void add(int x,int num,int w)
{while(x<=n) ins(1,tt,rt[x],num,w),x+=lowbit(x);}
void dfs(int x,int las) {
in[x]=++now,pre[x][0]=las,dep[x]=dep[las]+1,add(now,a[x],1);
for(int i=1;i<=17;++i) pre[x][i]=pre[pre[x][i-1]][i-1];
for(int i=h[x];i;i=ne[i]) if(to[i]!=las) dfs(to[i],x);
le[x]=now,add(now+1,a[x],-1);
}
int main()
{
int x,y,re;
n=read(),q=read();
for(int i=1;i<=n;++i) a[i]=b[++js]=read();
for(int i=1;i<n;++i) x=read(),y=read(),adde(x,y),adde(y,x);
for(int i=1;i<=q;++i) {
K[i]=read(),A[i]=read(),B[i]=read();
if(!K[i]) b[++js]=B[i];
}
sort(b+1,b+1+js);for(int i=2;i<=js;++i) if(b[i]!=b[tt]) b[++tt]=b[i];
for(int i=1;i<=n;++i) a[i]=getn(a[i]);
dfs(1,0);
for(int i=1;i<=q;++i) {
x=A[i],y=B[i];
if(!K[i]) {
B[i]=getn(B[i]),add(in[x],a[x],-1),add(le[x]+1,a[x],1);
add(in[x],B[i],1),add(le[x]+1,B[i],-1),a[x]=B[i];
}
else {
re=query(A[i],B[i],K[i]);
if(re>0) printf("%d\n",re);
else puts("invalid request!");
}
}
return 0;
}