首页 > 其他分享 >点分树(动态点分治) 学习笔记

点分树(动态点分治) 学习笔记

时间:2023-08-20 21:23:59浏览次数:53  
标签:maxx return int siz 分治 笔记 gc 点分树 maxn

模板题

题目传送门
给定一棵树(带点权),支持以下操作:
修改点权。
查询到一个点距离 \(\le k\) 的点的权值和。
\(n,T\le 10^5\)

算法解析

前置知识:点分治
我们考虑把每次求出的重心和上一层的重心连边,我们就可以得到点分树。
这棵树有以下性质:

  1. 树高为 \(\log n\),也就是暴力找 LCA 的复杂度、暴力从一个点一路跳到跟的复杂度都是 \(O(\log n)\)
  2. 所有节点为根的子树的大小和是 \(O(n\log n)\)
  3. 虽然点分树的边和原树的边没有任何关系,但是对于任意两个点 \(u,v\),在点分树上的两个点的 LCA \(x\) 一定在原树 \(u\) 到 \(v\) 的路径上。这样,对于原树中的路径统计,我们就可以在点分树上通过 \(u\to lca(u,v)\to v\) 来统计。

题目解析

对于每个点,维护一个树状数组来表示在点分树内的子树中到在这个点距离小于等于 \(i\) 的和,显然 \(i\le\) 子树大小,所以空间上可行。
然后考虑求值。我们发现往上跳的时候不能选择同一个子树中的点,所以还需要维护到在这个点父亲距离小于等于 \(i\) 的和用于容斥。
修改直接暴力跳到跟就好。
复杂度 \(O(n\log^2n)\)。(认为 \(n,T\) 同阶)

#include<vector>
#include<cstdio>
#include<iostream>
#include<assert.h>
#define db double
#define pc putchar
#define ll long long
#define Tp template<typename _T>
Tp _T mabs(_T a){ return a<0?-a:a; }
Tp _T mmax(_T a,_T b){ return a<b?b:a; }
Tp _T mmin(_T a,_T b){ return a<b?a:b; }
Tp void mswap(_T &a,_T &b){ _T t=a; a=b; b=t; return; }
using namespace std;
struct IO{
	char buf[1<<21],*p1,*p2;
	char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++; }
	IO & operator >> (char &x){ x=gc(); while(x=='\r'||x=='\n'||x==' ') x=gc(); return *this; }
	Tp IO & operator >> (_T &x){
		x=0; char c=gc(); bool f=0; while(c<'0'||c>'9'){ if(c=='-') f^=1; c=gc(); }
		while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc(); f?x=-x:0; return *this;
	}
	int st[39],top;
	IO & operator << (char x) { pc(x); return *this; }
	Tp IO & operator << (_T x){
		if(x<0) x=-x,pc('-'); if(x==0){ pc('0'); return *this; }
		top=0; while(x) st[++top]=x%10,x/=10;
		while(top--) pc(st[top+1]+48); return *this;
	}
}fin;
#define maxn 100039
int n,lgn,T,opt,u,v,ans,val[maxn],head[maxn],nex[maxn<<1],to[maxn<<1],kkk;
#define add(x,y) nex[++kkk]=head[x]; head[x]=kkk; to[kkk]=y
int dep[maxn],dfn[maxn],f[maxn<<1][22],lg[maxn<<1],cnt;
void dfs(int x,int pre){
	int i; dfn[x]=++cnt; f[cnt][0]=x;
	for(i=head[x];i;i=nex[i]) if(to[i]!=pre){
		dep[to[i]]=dep[x]+1; dfs(to[i],x); f[++cnt][0]=x;
	} return;
}
int js(int a,int b){ return dep[a]<dep[b]?a:b; }
int lca(int a,int b){ if(a>b) mswap(a,b); return js(f[a][lg[b-a+1]],f[b-(1<<lg[b-a+1])+1][lg[b-a+1]]); }
int getdis(int a,int b){ return dep[a]+dep[b]-dep[lca(dfn[a],dfn[b])]*2; }
vector<int> c[2][maxn];
int sum,root,minx,fa[maxn],siz[maxn],mark[maxn];
void getsiz(int x,int pre){
	sum++; int i; for(i=head[x];i;i=nex[i]) if(to[i]!=pre&&!mark[to[i]]) getsiz(to[i],x); return;
}
void getroot(int x,int pre){
	int i,maxx=-1; siz[x]=1; for(i=head[x];i;i=nex[i]) if(to[i]!=pre&&!mark[to[i]]){
		getroot(to[i],x); siz[x]+=siz[to[i]]; if(siz[to[i]]>maxx) maxx=siz[to[i]];
	} maxx=mmax(maxx,sum-siz[x]); if(maxx<minx) minx=maxx,root=x; return;
}
int work(int x){
	sum=0; getsiz(x,-1); minx=10000000; getroot(x,-1); int i,tmp=root;
	c[0][root].resize(sum+10),c[1][root].resize(sum+10);
	mark[root]=1; for(i=head[root];i;i=nex[i]) if(!mark[to[i]]) fa[work(to[i])]=tmp;
	return tmp;
}
#define lowbit(x) (x&-x)
void upd(int x,int y,int z,int w){
	z++; int t=c[x][y].size(); while(z<=t){ c[x][y][z-1]+=w; z+=lowbit(z); } return;
}
int que(int x,int y,int z){
	z++; z=mmin(z,(signed)c[x][y].size()); int s=0;
	while(z){ s+=c[x][y][z-1]; z-=lowbit(z); } return s;
}
void update(int x,int y){
	int i;
	for(i=x;i;i=fa[i])
		upd(0,i,getdis(x,i),y);
	for(i=x;fa[i];i=fa[i])
		upd(1,i,getdis(x,fa[i]),y);
}
void query(int x,int k){
	ans=0; int i,tmp; ans+=que(0,x,k);
	for(i=x;fa[i];i=fa[i]){
		tmp=getdis(x,fa[i]);
		if(k>=tmp) ans+=que(0,fa[i],k-tmp)-que(1,i,k-tmp);
	} return;
}
int main(){
	freopen("1.in","r",stdin);
	//freopen(".out","w",stdout);
	fin>>n>>T; int i,j; for(i=1;i<=n;i++) fin>>val[i];
	for(i=1;i<n;i++){ fin>>u>>v; add(u,v); add(v,u); }
	cnt=0; dfs(1,-1); for(i=1;i<=cnt;i++){ lg[i]=lg[i-1]; if((1<<(lg[i]+1))<=i) lg[i]++; }
	lgn=lg[cnt];
	for(j=1;j<=lgn;j++) for(i=1;i<=cnt;i++)
		if(i+(1<<j)-1<=cnt) f[i][j]=js(f[i][j-1],f[i+(1<<(j-1))][j-1]);
	work(1); for(i=1;i<=n;i++) update(i,val[i]);
	while(T--){
		fin>>opt>>u>>v; u^=ans,v^=ans; assert(1<=u&&u<=n);
		if(opt) update(u,v-val[u]),val[u]=v;
		else query(u,v),fin<<ans<<'\n';
	}
	return 0;
}

标签:maxx,return,int,siz,分治,笔记,gc,点分树,maxn
From: https://www.cnblogs.com/jiangtaizhe001/p/17644587.html

相关文章

  • MHATC系统笔记3
    Tip:1、NetRecord;参考链接:linux系统UDP丢包问题分析思路|CizixsWriteHere如何高效定位网络丢包问题?-知乎(zhihu.com)【翻译】理解TCP/IP网络栈|CizixsWriteHere1、tcpdump抓的包来自哪?内核TCPchecksum是网卡计算的,不是内核;如果有网络抓包工具(比如wireshark或......
  • 《Java编程思想第四版》学习笔记17
    崩溃JavaJava标准集合里包含了toString()方法,所以它们能生成自己的String表达方式,包括它们容纳的对象。例如在Vector中,toString()会在Vector的各个元素中步进和遍历,并为每个元素调用toString()。假定我们现在想打印出自己类的地址。看起来似乎简单地引用this即可(特别......
  • 我反对独立开发者做笔记产品:从商业角度看笔记产品市场竞争
    事情是这样的,刷掘金时看到这篇文章:良言难劝该死鬼,居然有人觉得独立开发做三件套是件好事,这篇文章提到了另一篇文章,是我很喜欢的一个公众号号主和菜头写的一篇《从番茄时钟和记账本开始》;之前在v2ex也看过相关讨论,于是打算好好思考下这件事情,于是在作者的文章下详细写了评价,一写写......
  • 学习笔记 - Java 面向对象_中
    this关键字当形参名和属性名相同时,使用this关键字来区分,有this修饰的变量是属性,无this修饰的是形参。this可以调用的除了属性,还有方法、构造器。所以,this指的是当前对象(在方法调用时)或当前正在创建的对象(在构造器中调用时)。在构造器中,使用this(形参列表);可以调用......
  • 笔记本电脑主板的细微伤痕:一场与微观世界的舞蹈
    引言:微小的伤痕,巨大的影响有一天,我在检查一台笔记本电脑时,发现了一个微小的细节——主板上的绝缘层有一点被磨损了。这样一个微不足道的伤口,竟然引领了我走入了一个丰富多彩的微观世界。第一幕:一个小小的问题,隐藏的危机伤口的解剖学:细微的危险在我们的笔记本电脑的主板(Motherb......
  • CSS笔记-盒子模型
    CSS笔记-盒子模型1.盒子模型css开发中,常常会提到一个词叫做“盒子”,这里的盒子专业名词叫“盒子模型(BoxModel)”,这一术语是从来设计和布局时使用的。通俗的讲,所有的HTML元素都可以看做是一个盒子;那么,将页面中所有的元素都设置成一个矩形的盒子后,对页面的布局就可以理解成把不......
  • Hadoop学习笔记、知识点搭建速过、包含Hadoop集群搭建、HDFS、IDE操作hadoop,DFSShell
    大数据概述......
  • 操作系统学习笔记
    Stanford:CS140使用操作系统概念CS162使用操作系统:设计与原理基础操作系统发展史原始操作系统在原始操作系统中,程序更多的是与硬件进行绑定,是一个无保护的标准服务库(为了方便用户或开发者使用而提供的一系列标准服务、函数或API)。系统一次只能运行一个程序多任务处理......
  • c++ 丢失笔记 [运算符重载、this指针、复制与拷贝构造、生存周期、箭头操作符]
    运算符重载、this指针、复制与拷贝构造、生存周期、箭头操作符有一部分是学校的OJ里做题需要就提前学了,然后没记笔记,有一部分是笔记丢了。不打算补这些笔记。不过还是在这里mark一下++运算符的重载。因为++运算符可以前置也可以后置,所以这里需要注意一下,如果是后置++,需要一个in......
  • C语言 笔记 1
    指针有什么用?场景A通过函数交换两个变量的值eg.交换变量a,b的值intswap(int*a,int*b){ inttemp=0; temp=*a; *a=*b; *b=temp;}场景B返回结果有多个,或return返回状态,指针返回结果intdivide(inta,intb,float*res){ intret=1; if(b!=0){ *res......