ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

点分树

2021-06-13 19:34:16  阅读:178  来源: 互联网

标签:get int siz top son re 点分树


点分树,也叫动态点分治,就是对于一个树性结构,按照重心的父子关系转化成一颗深度严格为logn的树

与普通点分治不同的是,他是动态的。。。就是可以在树上的节点进行权值更改,然后暴力得到答案

这个暴力可不是从根节点一直往下搜,而是利用某些数据结构,或者一个比较简单的递推式,又或是啥的,来简化暴力的过程

(优美的暴力)

所以我们对于重心的合理利用成了重点,当然,这也是点分治,容斥原理的利用还是非常重要的

震波

这个就是点分树最最最最基础的模板题了,就是要边地震边修改权值

然后加上稍微简单一点的查询

首先我们一定是要找到lca的,不然咋求两点之间的距离??

(个人习惯用树链剖分中的轻重链top来求)

int siz[N],dep[N],son[N],fa[N];
void dfs1(int x,int f){
    siz[x]=1;
    dep[x]=dep[f]+1;
    fa[x]=f;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f)continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
    }
}
int top[N];
void dfs2(int x,int f){
    top[x]=f;
    if(son[x])dfs2(son[x],f);
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==son[x]||y==fa[x])continue;
        dfs2(y,y);
    }
}
int get_lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}

这样我们就有一个模板可以利用来求两点之间的连线

所以get_dis

int get_dis(int x,int y){
    return dep[x]+dep[y]-2*dep[get_lca(x,y)];
}

接下来就是关于我们去找重心然后更新权值的过程了

找重心并且将以重心为树的连边的重心树建立,就是newfa[];

(重点是,每次的siz都要重新找,因为你的树已经重构了,原来的siz已经不适用了)

int rt,alsiz,ms[N],mx;
bool vis[N];
void get_rt(int x,int f){
    siz[x]=1;ms[x]=0;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f||vis[y])continue;
        get_rt(y,x);
        siz[x]+=siz[y];
        ms[x]=max(ms[x],siz[y]);
    }
    ms[x]=max(ms[x],alsiz-siz[x]);
    if(ms[x]<mx)mx=ms[x],rt=x;
}
int newfa[N];
void get_siz(int x,int f){
    siz[x]=1;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f||vis[y])continue;
        get_siz(y,x);
        siz[x]+=siz[y];
    }
}
void pre_dfs(int x){
    vis[x]=1;get_siz(x,0);
    int tmp=siz[x]+5;
    t1[x].init(tmp);t2[x].init(tmp);
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(vis[y])continue;
        alsiz=mx=siz[y];get_rt(y,x);
        newfa[rt]=x;pre_dfs(rt);
    }
}

接下来就是计算过程,我们要根据题意来计算,每一次更新之后的答案

这里运用树状数组来维护,当然线段树也可以,不过我觉得我的码量已经够大了,就别祸害我的编译器了

(这里数据范围过大,数组开不出,用vector,但是记住,在用他的时候,一定要先插入零)

 

tr[i].resize(x);

 

或者是我在下面代码写的那种,上面的是我在看题解的时候看到的,好像比我直接插入快好多~~~!!!

struct bit_tree{
    vector<int> tr;
    int trsiz;
    void init(int x){
        trsiz=x+1;for(re i=0;i<=trsiz;i++)tr.push_back(0);
    }
    void add(int x,int v){
        x=min(x+1,trsiz);
        for(re i=x;i<=trsiz;i+=(i&(-i)))tr[i]+=v;//cout<<i<<" ";
        //cout<<endl;
    } 
    int query(int x){
        x=min(x+1,trsiz);int ret=0;
        for(re i=x;i;i-=(i&(-i)))ret+=tr[i];
        return ret;
    }
}t1[N],t2[N];

然后就剩下最后的更新和查找了

这个题要更新的是每个点的权值,而且是覆盖式更新,所以我们要先做差,再更新

然后,为什么可以用树状数组呢?

每一次我们都以节点之间的距离为下标,更新这个点的树状数组;

查询的时候,直接根据限制的距离,在每一个树状数组中查询

记得容斥原理,定义两个树状数组,然后加爹的,减儿子的(这里指的是重心树中的父子关系)

void get_up(int x,int v){
    for(re i=x;i;i=newfa[i])t1[i].add(get_dis(i,x),v);
    //cout<<"sb"<<endl;
    for(re i=x;newfa[i];i=newfa[i])t2[i].add(get_dis(newfa[i],x),v);
}
int get_ans(int x,int len){
    int ret=t1[x].query(len);
    for(re i=x;newfa[i];i=newfa[i]){
        int tmp=get_dis(newfa[i],x);
        if(len>=tmp)ret+=t1[newfa[i]].query(len-tmp)-t2[i].query(len-tmp);
    }
    return ret;
}

完整代码

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=100005;
int n,m;
int val[N];
struct bit_tree{
    vector<int> tr;
    int trsiz;
    void init(int x){
        trsiz=x+1;for(re i=0;i<=trsiz;i++)tr.push_back(0);
    }
    void add(int x,int v){
        x=min(x+1,trsiz);
        for(re i=x;i<=trsiz;i+=(i&(-i)))tr[i]+=v;//cout<<i<<" ";
        //cout<<endl;
    } 
    int query(int x){
        x=min(x+1,trsiz);int ret=0;
        for(re i=x;i;i-=(i&(-i)))ret+=tr[i];
        return ret;
    }
}t1[N],t2[N];
int to[N<<1],nxt[N<<1],head[N<<1],rp;
void add_edg(int x,int y){
    to[++rp]=y;
    nxt[rp]=head[x];
    head[x]=rp;
}
int siz[N],dep[N],son[N],fa[N];
void dfs1(int x,int f){
    siz[x]=1;
    dep[x]=dep[f]+1;
    fa[x]=f;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f)continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
    }
}
int top[N];
void dfs2(int x,int f){
    top[x]=f;
    if(son[x])dfs2(son[x],f);
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==son[x]||y==fa[x])continue;
        dfs2(y,y);
    }
}
int get_lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
int get_dis(int x,int y){
    return dep[x]+dep[y]-2*dep[get_lca(x,y)];
}
int rt,alsiz,ms[N],mx;
bool vis[N];
void get_rt(int x,int f){
    siz[x]=1;ms[x]=0;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f||vis[y])continue;
        get_rt(y,x);
        siz[x]+=siz[y];
        ms[x]=max(ms[x],siz[y]);
    }
    ms[x]=max(ms[x],alsiz-siz[x]);
    if(ms[x]<mx)mx=ms[x],rt=x;
}
int newfa[N];
void get_siz(int x,int f){
    siz[x]=1;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f||vis[y])continue;
        get_siz(y,x);
        siz[x]+=siz[y];
    }
}
void pre_dfs(int x){
    vis[x]=1;get_siz(x,0);
    int tmp=siz[x]+5;
    t1[x].init(tmp);t2[x].init(tmp);
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(vis[y])continue;
        alsiz=mx=siz[y];get_rt(y,x);
        newfa[rt]=x;pre_dfs(rt);
    }
}
void get_up(int x,int v){
    for(re i=x;i;i=newfa[i])t1[i].add(get_dis(i,x),v);
    for(re i=x;newfa[i];i=newfa[i])t2[i].add(get_dis(newfa[i],x),v);
}
int get_ans(int x,int len){
    int ret=t1[x].query(len);
    for(re i=x;newfa[i];i=newfa[i]){
        int tmp=get_dis(newfa[i],x);
        if(len>=tmp)ret+=t1[newfa[i]].query(len-tmp)-t2[i].query(len-tmp);
    }
    return ret;
}
signed main(){
    scanf("%d%d",&n,&m);    
    for(re i=1;i<=n;i++)scanf("%d",&val[i]);
    for(re i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        add_edg(x,y);add_edg(y,x);
    }
    dfs1(1,0);
    dfs2(1,1);
    alsiz=mx=n;
    get_rt(1,0);
    pre_dfs(rt);
    for(re i=1;i<=n;i++)get_up(i,val[i]);
    int ans=0;
    for(re i=1;i<=m;i++){
        int typ,x,y;scanf("%d%d%d",&typ,&x,&y);
        x^=ans;y^=ans;
        if(typ)get_up(x,y-val[x]),val[x]=y;
        else printf("%d\n",ans=get_ans(x,y));
    }
}
View Code

 

标签:get,int,siz,top,son,re,点分树
来源: https://www.cnblogs.com/hzoi-fengwu/p/14880931.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有