[牛客练习赛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;
}
View Code

猜你喜欢

转载自www.cnblogs.com/judp/p/12704161.html