点分数
Posted 一只小予!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点分数相关的知识,希望对你有一定的参考价值。
点分树,也叫动态点分治,就是对于一个树性结构,按照重心的父子关系转化成一颗深度严格为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; }
完整代码
以上是关于点分数的主要内容,如果未能解决你的问题,请参考以下文章