[NOI2021] 轻重边题解
一眼数据结构
考虑树上有什么数据结构支持 \(x\) 到 \(y\) 节点的修改和查询,那就是:树链剖分。
那么这道树链剖分的题有个 \(trick\):边点转换&染色法,对于每次修改,考虑将修改路径上的点全部染上一种独一无二的颜色,而查询的时候,就查询路径上相邻的相同的颜色节点个数即可。
正确性易证:因为每次修改,该点周围的边都变成轻边除了那条路径上的边,因为那条路径上的点必然与该点颜色一样,而不是路径上的点必然与该点颜色不一样,所以可以转换成求路径上相邻的相同的颜色节点个数。
考虑怎么求:对于线段树上的点,开一个结构体,记 \(lc:\) 该区间左端点颜色,\(rc:\) 该区间右端点颜色,\(gs:\) 该区间相邻相同颜色节点个数,\(tag:\) 懒标记(要区间修改)
一些小细节:
\(1\),初始化时把所有数组都初始化了,防止出现神奇错误(比如我为此调了半小时),不差这点常数。
\(2\),要特别注意 \(top_x=top_y\) 的情况,大部分错误都出现在这里。
上代码:
#include<bits/stdc++.h> #define ll long long using namespace std; const ll N=1e5+50; ll T,n,m,u,v; vector<ll> e[N]; ll dep[N],fa[N],siz[N],son[N]; void dfs1(ll wz,ll last) { dep[wz]=dep[last]+1; fa[wz]=last; siz[wz]=1; ll bigson=0; for(ll i=0;i<e[wz].size();i++) { ll j=e[wz][i]; if(j==last) continue; dfs1(j,wz); siz[wz]+=siz[j]; if(siz[j]>bigson) bigson=siz[j],son[wz]=j; } } ll top[N],id[N],cnt; void dfs2(ll wz,ll topf) { top[wz]=topf; id[wz]=++cnt; if(!son[wz]) return ; dfs2(son[wz],topf); for(ll i=0;i<e[wz].size();i++) { ll j=e[wz][i]; if(j==fa[wz]||j==son[wz]) continue; dfs2(j,j); } } struct jgt { ll lc,rc,gs,tag; }tr[N*8]; ll idx; void pushup(jgt &wz,jgt l,jgt r) { wz.gs=l.gs+r.gs+(l.rc==r.lc); wz.rc=r.rc; wz.lc=l.lc; } void BT(ll wz,ll l,ll r) { if(l==r) { tr[wz].lc=l; tr[wz].rc=l; tr[wz].gs=0; tr[wz].tag=0; return ; } ll mid=(l+r)/2; BT(wz*2,l,mid); BT(wz*2+1,mid+1,r); pushup(tr[wz],tr[wz*2],tr[wz*2+1]); } void pushdown(ll wz,ll l,ll r) { ll mid=(l+r)/2; tr[wz*2].gs=mid-l; tr[wz*2].rc=tr[wz].tag; tr[wz*2].lc=tr[wz].tag; tr[wz*2].tag=tr[wz].tag; tr[wz*2+1].gs=r-mid-1; tr[wz*2+1].rc=tr[wz].tag; tr[wz*2+1].lc=tr[wz].tag; tr[wz*2+1].tag=tr[wz].tag; tr[wz].tag=0; } void gai(ll wz,ll l,ll r,ll le,ll ri,ll co) { if(le<=l&&ri>=r) { tr[wz].gs=r-l; tr[wz].rc=co; tr[wz].lc=co; tr[wz].tag=co; return ; } if(tr[wz].tag&&l!=r) pushdown(wz,l,r); ll mid=(l+r)/2; if(le<=mid) gai(wz*2,l,mid,le,ri,co); if(ri>mid) gai(wz*2+1,mid+1,r,le,ri,co); pushup(tr[wz],tr[wz*2],tr[wz*2+1]); } jgt query(ll wz,ll l,ll r,ll le,ll ri) { if(le<=l&&ri>=r) return tr[wz]; if(tr[wz].tag&&l!=r) pushdown(wz,l,r); ll mid=(l+r)/2; if(le<=mid&&ri>mid) { jgt t1=query(wz*2,l,mid,le,ri); jgt t2=query(wz*2+1,mid+1,r,le,ri); jgt t3; pushup(t3,t1,t2); return t3; } if(le<=mid) return query(wz*2,l,mid,le,ri); if(ri>mid) return query(wz*2+1,mid+1,r,le,ri); } void upd(ll x,ll y,ll co) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); gai(1,1,n,id[top[x]],id[x],co); x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); gai(1,1,n,id[x],id[y],co); } ll updQ(ll x,ll y) { ll ans1=0,ans2=0,co1=0,co2=0,w=0; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y),w^=1; jgt tzy=query(1,1,n,id[top[x]],id[x]); if(w==1) { ans2=ans2+tzy.gs+(tzy.rc==co2); co2=tzy.lc; } else { ans1=ans1+tzy.gs+(tzy.rc==co1); co1=tzy.lc; } x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y),w^=1; jgt tzy=query(1,1,n,id[x],id[y]); if(w==0) return (ans1+ans2+tzy.gs+(tzy.lc==co1)+(tzy.rc==co2)); else return (ans1+ans2+tzy.gs+(tzy.lc==co2)+(tzy.rc==co1)); } void Init() { cnt=0,idx=0; memset(son,0,sizeof son); memset(fa,0,sizeof fa); memset(siz,0,sizeof siz); memset(dep,0,sizeof dep); memset(top,0,sizeof top); memset(id,0,sizeof id); memset(tr,0,sizeof tr); for(ll i=1;i<=n;i++) e[i].clear(); } int main() { scanf("%lld",&T); while(T--) { scanf("%lld %lld",&n,&m); for(ll i=1;i<n;i++) { scanf("%lld %lld",&u,&v); e[u].push_back(v); e[v].push_back(u); } dfs1(1,0); dfs2(1,1); BT(1,1,n); idx=n; ll opt,x,y; for(ll i=1;i<=m;i++) { scanf("%lld %lld %lld",&opt,&x,&y); if(opt==1) upd(x,y,++idx); if(opt==2) printf("%lld\n",updQ(x,y)); } Init(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?