zoukankan      html  css  js  c++  java
  • 牛客练习赛61 F苹果树题解

    一.闲话

    学了淀粉质有一年多了,期间基本没用过,又因为当时是直接背的代码,导致打比赛的时候对这道题根本无从下手,甚至都没想到是淀粉质qwq

    然后,今天去学了一下淀粉质,发现原理和实现特别简单,快速码了个模板后,又去顺带学了下点分树,就来搞这道题了。。。

    二.题解

    这道题,我们看数据范围,发现成熟度的大小非常的小,所以,我们考虑下如何计算答案。

    假设数据随机,我们把树减号后,期望树高是logn。

    这样,我们可以,对于每一个点,我们算出他的子树里面所有点到这个子树的距离。复杂度(O(nlogn))【每个点最多只有logn个祖先,所以每个点最多被算logn次】

    然后,求出这个之后,我们就可以开一个动态加点线段树来做了,我们对每个点i开一点动态加点线段树,里面每个节点表示的是,i的子树中,成熟度在l-r的距离i最小的节点到i的距离

    同样使用上面的证明方法,每个点最多被放进logn棵线段树中,所以,加上线段树的复杂度就是:(O(nlog^2n))

    再考虑1操作,其实和上面存值的操作一样的。

    关于2操作,我们可以求出u的所有祖先(包括u)的子树中,成熟度为l-r的点到这个点的最小距离(访问一遍线段树即可),然后加上u到这个点的距离就行了,最后所有答案取最小再乘2(还要走回去)就是我们要求的答案了~

    可能有些人有点疑惑,假如,我们对于u的一个祖先x,他的子树中成熟度符合条件的到u距离最小的点到x的路径中是有可能会走到u到x的路径的一部分的,这样的话,我们计算的时候,那部分不就重复计算了吗?但是,画个图我们就会发现,其实如果我们有部分走重了话那么,我们计算既是x的儿子,又是u的祖先的那个点y时,我们就少走了一段y到x的距离,所以,我们在算y的时候的答案就会算x的时候小,同理,如果y也有重叠的话,另一个点z就会比y算的小。。。由于我们是取最小,所以最终我们的答案一定没有重叠部分

    然而,我们的做法有一个前提,就是数据随机。

    如果给出一个链的话,复杂度就会原地爆炸

    怎么办呢?

    这里,就要用到一个神奇的东西了——点分树

    点分树是什么东西?

    我们先把原来的树的重心x做为根,在把x的各个儿子所在的子树的重心作为x的儿子。。。一直递归下去,最终弄出来的树就是点分树。

    点分树很神奇,因为我们是把子树的重心作为根,所以,最终点分树的树高一定是logn级别的

    而此题,我们其实并不需要知道我们诸如谁是谁的父亲之类的信息,我们需要知道的只是一个点到其子树所有点的距离,而这个东西,我们是可以在构造点分树时,一遍dfs直接求出来,所以我们可以建一个树高为logn的点分树,再用之前的方法,构造动态开点线段树就可以解决此题了~

    代码:

    #pragma GCC optimize(3,"inline","Ofast")
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+1,inf=2e9,M=1e7+1;
    struct node{
        int v,w,nex;
    }t[N<<1];
    struct zxs{
        int lson,rson,w;
    }p[M];
    int dis[N][30],dep[N];//i到深度为j的祖先的距离
    int fa[N];
    int val[N],siz[N],tot,root;
    int w[N],las[N],len;
    bool vis[N];
    int id[N];
    inline void add(int u,int v,int w){
        t[++len]=(node){v,w,las[u]},las[u]=len;
    }
    inline void find_zx(int now,int fu){
        siz[now]=1;val[now]=0;
        for(int i=las[now];i;i=t[i].nex){
            int v=t[i].v;
            if(!vis[v]&&fu!=v){
                find_zx(v,now);
                siz[now]+=siz[v];
                val[now]=max(val[now],siz[v]);
            }
        }
        val[now]=max(val[now],tot-siz[now]);
        if(val[now]<val[root]){
            root=now;
        }
    }
    int num;
    inline void find_dis(int now,int fu,int Dis){
        dis[now][num]=Dis;
        for(int i=las[now];i;i=t[i].nex){
            int v=t[i].v;
            if(vis[v]||v==fu){
                continue;
            }
            find_dis(v,now,Dis+t[i].w);
        }
    }
    inline void work(int now,int fu){
        vis[now]=1;fa[now]=fu;num=dep[now];
        find_dis(now,now,0);
        for(int i=las[now];i;i=t[i].nex){
            int v=t[i].v;
            if(vis[v]){
                continue;
            }
            root=0;tot=siz[v],find_zx(v,v);
            dep[root]=dep[now]+1;
            work(root,now);
        }
    }
    int cnt;
    inline void insert(int &now,int l,int r,int x,int y){
        if(!now){
            now=++cnt;p[now].w=inf;
        }
        p[now].w=min(p[now].w,y);
        if(l==r){
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid){
            insert(p[now].lson,l,mid,x,y);
        }else{
            insert(p[now].rson,mid+1,r,x,y);
        }
    }
    inline void Insert(int x,int val){
        int now=x;
        while(now){
            insert(id[now],1,10000,val,dis[x][dep[now]]),now=fa[now];
        }
    }
    inline int find(int now,int l,int r,int lc,int rc){
        if(!now){
            return inf;
        }
        if(lc<=l&&r<=rc){
            return p[now].w;
        }
        int mid=(l+r)>>1,res=inf;
        if(lc<=mid){
            res=min(res,find(p[now].lson,l,mid,lc,rc));
        }
        if(rc>mid){
            res=min(res,find(p[now].rson,mid+1,r,lc,rc));
        }
        return res;
    }
    inline int Find(int x,int l,int r){
        int now=x,ans=inf;
        while(now){
            ans=min(ans,find(id[now],1,10000,l,r)+dis[x][dep[now]]),now=fa[now];
        }
        return ans>=inf?-1:ans;
    }
    inline int read(){
    	char ch=getchar();int w=0,ss=0;
    	while(ch<'0'||ch>'9')w|=(ch=='-'),ch=getchar();
    	while(ch>='0'&&ch<='9')ss=(ss*10+(ch-'0')),ch=getchar();
    	return w?-ss:ss;
    }
    inline void write(int x){
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(char(x%10+'0'));
    }
    int main(){
        int n=read(),m=read();
        for(int i=1;i<=n;++i){
            w[i]=read();
        }
        for(int i=1;i<n;++i){
            int u=read(),v=read(),w=read();
            add(u,v,w),add(v,u,w);
        }
        tot=n;
        val[root]=inf;
        find_zx(1,1);
        work(root,0);
        for(int i=1;i<=n;++i){
            Insert(i,w[i]);
        }
        while(m--){
            int opt=read();
            if(opt==1){
                int u=read(),x=read();
                Insert(u,x);
            }else{
                int u=read(),x=read(),y=read();
                int res=Find(u,x,y);
                if(res==-1){
                	puts("-1");
                }else{
                	write(res*2),putchar('
    ');
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    selector
    Shape 属性解释
    Button 自定义图片,代码绘制样式,添加音效的方法
    android中得到颜色,图片资源的方式
    EditText 中文文档
    EditText 限制输入,自定义样式,监听输入的字符,自动换行
    TextView字体,行距,html格式,超链接,对大长度的设定
    TextView 中文文档
    Eclipse 汉化的和修改字体的方法
    让Android SDK Manager正常更新的办法
  • 原文地址:https://www.cnblogs.com/ThinkofBlank/p/12719019.html
Copyright © 2011-2022 走看看