专栏文章

P11755 [COCI 2024/2025 #5] 树树 2 / Stablo II题解

P11755题解参与者 3已保存评论 3

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
3 条
当前快照
1 份
快照标识符
@miq7qqvb
此快照首次捕获于
2025/12/04 00:20
3 个月前
此快照最后确认于
2025/12/04 00:20
3 个月前
查看原文
一眼就认出了正解是树剖,甚至比【模板】重链剖分/树链剖分还简单。
时间复杂度 O(nlog2n)O(n\log^2{n}),卡一下常就能过。
如果会树剖的话只要边权转点权就行了。
我们可以把一条边的边权当作这条边深度较大的节点的点权。
如图,以样例 11 最终结果为例,节点内左边较大数字为编号,右边较小数字为转化后的点权,边上数字为边权。

AC code

CPP
#include<bits/stdc++.h>
#define int long long
#define nc() getchar()
using namespace std;
const int N=1e6+5;
int n,q,t[N<<2],uu[N],vv[N];
int head[N],ver[N<<1],nxt[N<<1],tot=0;//链式前向星三件套 
int top[N],id[N],dep[N],fa[N],size[N],son[N],tot2=0;//朴实无华的树剖 
//top重链的开端 
//id在线段树中的位置 
//dep节点深度
//fa节点父亲
//size子树大小 
//son重儿子 
inline int read(){
	int x=0,f=1;
	char ch=nc();
	while(ch<48||ch>57){
		if(ch=='-') f=-1;
		ch=nc();
	}
	while(ch>=48&&ch<=57) x=x*10+ch-48,ch=nc();
	return x*f;
}
inline void write(int x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
void add(int x,int y){//加边 
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void pushdown(int k,int l,int r,int mid){
	if(!t[k]) return;
	t[k<<1]=t[k];
	t[k<<1|1]=t[k];
	t[k]=0;
}
void change(int k,int l,int r,int x,int y,int z){//区间修改 
	if(x<=l && r<=y){
		t[k]=z;
		return;
	}
	int mid=l+r>>1;
	pushdown(k,l,r,mid);
	if(x<=mid) change(k<<1,l,mid,x,y,z);
	if(mid<y) change(k<<1|1,mid+1,r,x,y,z);
}
void change_chain(int x,int y,int z){//将x到y的路径上的边权修改为z 
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		change(1,1,n,id[top[x]],id[x],z);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	if(x!=y) change(1,1,n,id[x]+1,id[y],z);
	//注意是in[x]+1,x和y的最近公共祖先的点权不修改 
}
int query(int k,int l,int r,int x){//单点查询
	if(l==r) return t[k];
	int mid=l+r>>1;
	pushdown(k,l,r,mid);
	if(x<=mid) return query(k<<1,l,mid,x);
	else return query(k<<1|1,mid+1,r,x);
}
void dfs1(int u,int father){
	fa[u]=father;dep[u]=dep[fa[u]]+1;size[u]=1;
	for(int i=head[u];i;i=nxt[i]){
		int v=ver[i];
		if(v==fa[u]) continue;
		dfs1(v,u);
		size[u]+=size[v];
		if(size[v]>size[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int top_){ 
	id[u]=++tot2;top[u]=top_;
	if(son[u]) dfs2(son[u],top_);
	for(int i=head[u];i;i=nxt[i]){
		int v=ver[i];
		if(v==fa[u] || v==son[u]) continue;
		dfs2(v,v);
	}
}
signed main(){
	cin>>n>>q;
	for(int i=1;i<n;i++){
		uu[i]=read();vv[i]=read();//记录第i条边 
		add(uu[i],vv[i]);add(vv[i],uu[i]);
	}
	dfs1(1,0);dfs2(1,1);
	for(int i=1;i<=q;i++){
		int x=read(),y=read();
		change_chain(x,y,i);
	}
	for(int i=1;i<n;i++){
		if(dep[uu[i]]>dep[vv[i]]) write(query(1,1,n,id[uu[i]])),putchar(' ');
		else write(query(1,1,n,id[vv[i]])),putchar(' ');
		//一条边中深度较大的节点点权即这条边的边权
//不懂的话看上面的图 
	}
}

评论

3 条评论,欢迎与作者交流。

正在加载评论...