P7735-[NOI2021]轻重边【树链剖分,线段树】

0|1前言

之前线上赛就A的题现在才写博客
请添加图片描述


1|0正题

题目链接:https://www.luogu.com.cn/problem/P7735


1|1题目大意

n个点的一棵树,开始所有边都是轻边,m次操作。

  1. xy路径上所有点连接的重边都变为轻边,然后再把路径上的边变成重边。
  2. 询问一条路径上的重边数量。

1T3,1n,m105


1|2解题思路

前随便找个点当根,我们用每个点去储存它连向它父节点边的信息。

然后考虑如何进行操作,发现是树上的路径操作,考虑树链剖分。

*为了方便描述我们将树链剖分的轻重边用加粗进行描述

首先我们可以先把路径上所有边(所对应的点储存的信息)都改成重边,那么问题就出在我们如何把连接的重边改成轻边。

暴力修改这些边显然不可行,我们注意到树链剖分后的我们可以方便的修改重边,而一条路径上的轻边路径不多,所以我们可以考虑只统一维护重边信息,而轻边信息我们可以在查询的时候再处理。

那么做法就很显然了,对于重边的信息我们用线段树修改,而对于轻边,我们再开一个线段树记录每个端点上次被覆盖的路径编号。

如果轻边所连接的两个端点是被不同路径覆盖的,那么这条边就是轻边,不然就是重边。

时间复杂度:O(mlog2n)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cctype> using namespace std; const int N=1e5+10; int read(){ int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-f;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+c-48;c=getchar();} return x*f; } struct node{ int to,next; }a[N<<1]; int T,n,m,cnt,tot,ls[N],fa[N],dep[N]; int rfn[N],ed[N],siz[N],son[N],top[N],id[N]; void addl(int x,int y){ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot;return; } void dfs(int x){ rfn[x]=++cnt;siz[x]=1; dep[x]=dep[fa[x]]+1; for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; if(y==fa[x])continue; fa[y]=x;dfs(y); siz[x]+=siz[y]; if(siz[y]>siz[son[x]])son[x]=y; } ed[x]=cnt; return; } void dFs(int x){ id[x]=++cnt; if(son[x]){ top[son[x]]=top[x]; dFs(son[x]); } for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; if(y==fa[x]||y==son[x])continue; top[y]=y;dFs(y); } return; } struct SegTree{ int w[N<<2],lazy[N<<2]; void Clear(){ memset(w,0,sizeof(w)); memset(lazy,0,sizeof(lazy)); return; } void Downdata(int x,int l,int r){ if(!lazy[x])return;int mid=(l+r)>>1; w[x*2]=(mid-l+1)*lazy[x]; w[x*2+1]=(r-mid)*lazy[x]; lazy[x*2]=lazy[x*2+1]=lazy[x]; lazy[x]=0;return; } int Ask(int x,int L,int R,int l,int r){ if(L==l&&R==r)return w[x]; int mid=(L+R)>>1;Downdata(x,L,R); if(r<=mid)return Ask(x*2,L,mid,l,r); if(l>mid)return Ask(x*2+1,mid+1,R,l,r); return Ask(x*2,L,mid,l,mid)+Ask(x*2+1,mid+1,R,mid+1,r); } void Change(int x,int L,int R,int l,int r,int val){ if(L==l&&R==r){w[x]=(R-L+1)*val;lazy[x]=val;return;} int mid=(L+R)>>1;Downdata(x,L,R); if(r<=mid)Change(x*2,L,mid,l,r,val); else if(l>mid)Change(x*2+1,mid+1,R,l,r,val); else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val); w[x]=w[x*2]+w[x*2+1]; } }Tw,Tl; void Updata(int x,int y,int pos){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); if(top[x]!=x)Tw.Change(1,1,n,id[top[x]]+1,id[x],1); Tl.Change(1,1,n,id[top[x]],id[x],pos); if(son[x])Tw.Change(1,1,n,id[x]+1,id[x]+1,0); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); Tl.Change(1,1,n,id[x],id[y],pos); if(id[x]!=id[y])Tw.Change(1,1,n,id[x]+1,id[y],1); if(son[y])Tw.Change(1,1,n,id[y]+1,id[y]+1,0); if(top[x]!=x)Tw.Change(1,1,n,id[x],id[x],0); } bool check(int x){ int p=Tl.Ask(1,1,n,id[x],id[x]); if(!p)return 0; return (p==Tl.Ask(1,1,n,id[fa[x]],id[fa[x]])); } int Ask(int x,int y){ int ans=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); ans+=Tw.Ask(1,1,n,id[top[x]],id[x]); x=top[x];ans+=check(x); x=fa[x]; } if(dep[x]>dep[y])swap(x,y); if(id[x]!=id[y])ans+=Tw.Ask(1,1,n,id[x]+1,id[y]); return ans; } int main() { T=read(); while(T--){ tot=0; memset(ls,0,sizeof(ls)); memset(fa,0,sizeof(fa)); memset(son,0,sizeof(son)); Tl.Clear();Tw.Clear(); n=read();m=read(); for(int i=1;i<n;i++){ int x=read(),y=read(); addl(x,y);addl(y,x); } cnt=0;dfs(1);cnt=0; top[1]=1;dFs(1);cnt=0; while(m--){ int op=read(),x=read(),y=read(); if(op==1)++cnt,Updata(x,y,cnt); else cout<<Ask(x,y)<<'\n'; } } return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15890192.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示