[牛客练习赛61F]苹果树——点分树,动态开点线段树
苹果树
把点分治的过程所有重心连成一颗树,那么这颗树高为logn。预处理重建树上每个节点到所有子节点的距离(原树上的距离)。对于每次查询和修改,在重建树上不断往上跳,每个节点开一个动态开点的线段树,进行查询。由于树高是logn的,所以时间复杂度为nlognlogn。空间复杂度也为nlognlogn。两点间距离可以用map存,可以用lca,也可以存每个节点到第i层的父节点的距离。
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; const int M=1e5+10; const int maxn=1e7; const int INF=0x3f3f3f3f; int head[N],ver[2*M],edge[2*M],nex[2*M],tot=1; inline void add(int x,int y,int z) { ver[++tot]=y,edge[tot]=z,nex[tot]=head[x],head[x]=tot; } int lc[maxn],rc[maxn],Min[maxn],cnt=0,rt[N];//rt:线段树根 void insert(int &o,int pos,int v,int l,int r){ if(o==0)Min[o=++cnt]=INF; if(l==r){Min[o]=min(Min[o],v);return;} int m=(l+r)>>1; if(pos<=m)insert(lc[o],pos,v,l,m); else insert(rc[o],pos,v,m+1,r); Min[o]=min(Min[lc[o]],Min[rc[o]]); } int query(int o,int a,int b,int l,int r){ if(o==0)return INF; if(a<=l&&b>=r)return Min[o]; int m=(l+r)>>1,res=INF; if(a<=m)res=query(lc[o],a,b,l,m); if(b>m)res=min(res,query(rc[o],a,b,m+1,r)); return res; } int vis[N],sz[N],sub_sz,max_sz,root; void get_root(int x,int fa){ int max_part=0;sz[x]=1; for(int i=head[x];i;i=nex[i]){ int y=ver[i];if(y==fa||vis[y])continue; get_root(y,x);sz[x]+=sz[y]; max_part=max(max_part,sz[y]); } max_part=max(max_part,sub_sz-sz[x]); if(max_part<max_sz){max_sz=max_part;root=x;} } int v[N],fa[N],dep[N],dis[N][20]; void get_dis(int x,int fa,int d){ dis[x][dep[root]]=d;//这里root刚好是重心 insert(rt[root],v[x],d,1,N); for(int i=head[x];i;i=nex[i]){ int y=ver[i];if(y==fa||vis[y])continue; get_dis(y,x,d+edge[i]); } } void divide(int x,int depth){ vis[x]=1; dep[x]=depth; get_dis(x,0,0); int tot_sz=sub_sz;//当前处理的树的大小 for(int i=head[x]; i; i=nex[i]) { int y=ver[i];if(vis[y])continue; max_sz=INF; sub_sz=sz[y]>sz[x]?tot_sz-sz[x]:sz[y];//获取子树大小 get_root(y,0); fa[root]=x; divide(root,depth+1); } } int main(){ int n,m,op,u,x,y,z; Min[0]=INF; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i)scanf("%d",v+i); for(int i=1;i<n;++i){ scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } sub_sz=n;max_sz=INF; get_root(1,0); divide(root,0); while(m--){ scanf("%d",&op); if(op==1){ scanf("%d%d",&x,&y);z=x; while(z)insert(rt[z],y,dis[x][dep[z]],1,N),z=fa[z]; }else{ scanf("%d%d%d",&u,&x,&y); int ans=INF;z=u; while(z)ans=min(ans,dis[u][dep[z]]+query(rt[z],x,y,1,N)),z=fa[z]; if(ans==INF)puts("-1"); else printf("%d\n",2*ans); } } return 0; }