专栏文章
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 个月前
一眼就认出了正解是树剖,甚至比【模板】重链剖分/树链剖分还简单。
时间复杂度 ,卡一下常就能过。
如果会树剖的话只要边权转点权就行了。
我们可以把一条边的边权当作这条边深度较大的节点的点权。
如图,以样例 最终结果为例,节点内左边较大数字为编号,右边较小数字为转化后的点权,边上数字为边权。


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 条评论,欢迎与作者交流。
正在加载评论...