【BZOJ4539】【HNOI2016】—树(主席树)

本文介绍了一种名为“树套树”的高效数据结构算法,该算法主要用于处理涉及树形结构的复杂查询问题。通过在新树上增加节点记录原树信息,并使用主席树进行辅助查询,实现了O(nlogn)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

真 ⋅ 树 套 树 真·树套树

因为每次复制的都是原树的子树

所以我们考虑直接在新树上加一个点来记录这是原树中的哪个点的子树
边权设为儿子在父亲子树中连接的点到父亲的根的距离

至于编号是一段连续的区间,可以直接记录每一段二分查找
具体的点相当于原树子树中查找第 k k k大,一个主席树记录一下
查询距离的时候先计算出 u , v , l c a u,v,lca u,v,lca三个根之间的距离

然后分类讨论一下 u , v u,v u,v l c a lca lca子树中连接的点之间的距离和真正点与根的距离

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
实现可以看代码

注意开 l o n g   l o n g long\ long long long

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
	return res*f;
}
inline ll readl(){
	char ch=getchar();
	ll res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=100005,Log=22;
int in[N],out[N],dfn,n,m,q,idx,fa[N],rt[N];
ll cnt[N];
#define mid ((l+r)>>1)
struct Pres_Tree{
	int rt[N],siz[N*Log],lc[N*Log],rc[N*Log],tot;
	void insert(int &u,int r1,int l,int r,int k){
		u=++tot,siz[u]=siz[r1]+1,lc[u]=lc[r1],rc[u]=rc[r1];
		if(l==r)return;
		if(k<=mid)insert(lc[u],lc[r1],l,mid,k);
		else insert(rc[u],rc[r1],mid+1,r,k);
	}
	inline void ins(int p,int k){insert(rt[p],rt[p-1],1,n,k);}
	int query(int r1,int r2,int l,int r,int k){
		if(l==r)return l;
		int del=siz[lc[r2]]-siz[lc[r1]];
		if(del>=k)return query(lc[r1],lc[r2],l,mid,k);
		else return query(rc[r1],rc[r2],mid+1,r,k-del);
	}
	inline int qry(int l,int r,int k){return query(rt[l-1],rt[r],1,n,k);}
}T;
struct G{
	int adj[N<<1],nxt[N<<1],to[N<<1],val[N<<1],dep[N],siz[N],f[N][Log],cnt;
	ll dis[N];
	inline void addedge(int u,int v,int w){
		nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v,val[cnt]=w;
	}
	inline void add(int u,int v,int w){
		addedge(u,v,w),addedge(v,u,w);
	}
	void dfs1(int u){
		for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1];
		for(int e=adj[u];e;e=nxt[e]){
			int v=to[e];
			if(v==f[u][0])continue;
			f[v][0]=u,dep[v]=dep[u]+1;
			dis[v]=dis[u]+val[e],dfs1(v);
		}
	}
	void dfs2(int u){
		siz[u]=1,in[u]=++dfn,T.ins(dfn,u);
		for(int e=adj[u];e;e=nxt[e]){
			int v=to[e];
			if(v==f[u][0])continue;
			dfs2(v),siz[u]+=siz[v];	
		}
		out[u]=dfn;
	}
	inline int up(int u,int k){
		for(int i=20;~i;i--){
			if(k&(1<<i))u=f[u][i];
		}
		return u;
	}
	inline int Lca(int u,int v){
		if(dep[u]<dep[v])swap(u,v);
		u=up(u,dep[u]-dep[v]);
		if(u==v)return u;
		for(int i=20;~i;i--)
		if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
		return f[u][0];
	}
	inline ll dist(int u,int v){
		return dis[u]+dis[v]-2ll*dis[Lca(u,v)];
	}
	inline int firson(int u,int v){
		return up(u,dep[u]-dep[v]-1);
	} 
}A,B;
inline int getid(ll u){
	return lower_bound(cnt+1,cnt+idx+1,u)-cnt;
}
inline ll query(ll u,ll v){
	int id1=getid(u),r1=rt[id1];u=T.qry(in[r1],out[r1],u-cnt[id1-1]);
	int id2=getid(v),r2=rt[id2];v=T.qry(in[r2],out[r2],v-cnt[id2-1]);
	int lca=B.Lca(id1,id2);
	if(id1==id2)return A.dist(u,v);
	ll res=B.dist(id1,id2)+A.dis[u]-A.dis[r1]+A.dis[v]-A.dis[r2];
	if(id1==lca){
		int fir=fa[B.firson(id2,id1)];
		res-=(A.dis[u]-A.dis[r1])+(A.dis[fir]-A.dis[r1])-A.dist(u,fir);
	}
	else if(id2==lca){
		int fir=fa[B.firson(id1,id2)];
		res-=(A.dis[v]-A.dis[r2])+(A.dis[fir]-A.dis[r2])-A.dist(v,fir);
	}
	else{
		int fira=fa[B.firson(id1,lca)],firb=fa[B.firson(id2,lca)];
		res-=(A.dis[fira]-A.dis[rt[lca]]+A.dis[firb]-A.dis[rt[lca]]-A.dist(fira,firb));
	}
	return res;
}
signed main(){
	n=read(),m=read(),q=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		A.add(u,v,1);
	}
	A.dfs1(1),A.dfs2(1);
	cnt[1]=n,idx=1,rt[1]=1;ll newn=n;
	for(int i=2;i<=m+1;i++){
		ll u=readl(),v=readl();
		int id=getid(v),root=rt[id];
		rt[i]=u,idx=i,fa[i]=T.qry(in[root],out[root],v-cnt[id-1]);
		B.add(i,id,A.dis[fa[i]]-A.dis[root]+1);
		newn+=A.siz[u],cnt[i]=newn;
	}B.dfs1(1);
	for(int i=1;i<=q;i++){
		ll u=readl(),v=readl();
		cout<<query(u,v)<<'\n';
	}
}
资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在 IT 领域,文档格式转换是常见需求,尤其在处理多种文件类型时。本文将聚焦于利用 Java 技术栈,尤其是 Apache POI 和 iTextPDF 库,实现 doc、xls(涵盖 Excel 2003 及 Excel 2007+)以及 txt、图片等格式文件向 PDF 的转换,并实现在线浏览功能。 先从 Apache POI 说起,它是一个强大的 Java 库,专注于处理 Microsoft Office 格式文件,比如 doc 和 xls。Apache POI 提供了 HSSF 和 XSSF 两个 API,其中 HSSF 用于读写老版本的 BIFF8 格式(Excel 97-2003),XSSF 则针对新的 XML 格式(Excel 2007+)。这两个 API 均具备读取和写入工作表、单元格、公式、样式等功能。读取 Excel 文件时,可通过创建 HSSFWorkbook 或 XSSFWorkbook 对象来打开相应格式的文件,进而遍历工作簿中的每个 Sheet,获取行和列数据。写入 Excel 文件时,创建新的 Workbook 对象,添加 Sheet、Row 和 Cell,即可构建新 Excel 文件。 再看 iTextPDF,它是一个用于生成和修改 PDF 文档的 Java 库,拥有丰富的 API。创建 PDF 文档时,借助 Document 对象,可定义页面尺寸、边距等属性来定制 PDF 外观。添加内容方面,可使用 Paragraph、List、Table 等元素将文本、列表和表格加入 PDF,图片可通过 Image 类加载插入。iTextPDF 支持多种字体和样式,可设置文本颜色、大小、样式等。此外,iTextPDF 的 TextRenderer 类能将 HTML、
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值