校门外歪脖树上的鸽子
思路很简单,但非常考验代码能力
思路
假设对区间[2,8]进行操作
路径由lca 15分成左右俩条链。将左端点2跳到最右上17,对左链17-16-10-15上每个作为左儿子的节点的兄弟操作。
于是构造新树:节点的新父亲为最近异侧祖先的同侧儿子
(手画略丑请忽略)
链上区间操作——树链剖分
一共有三棵树:题目所给原树,构造新树,树链剖分所用线段树
来整合一下需要的操作,便于理清代码思路,并代替行间注释
1、dfs()遍历原树 cnt[]子树所含叶子数,即节点包含区间长度 dep[]深度 Fa[][]倍增求lca
2、Do()连边构造新树
3、树链剖分fhe() Son[]重儿子 siz[]新树子树大小 fa[]新树父亲
che() s[]新树cnt前缀和 dfn[] top[]
4、操作
getlca()
L,R跳到同侧最高
如果L,R都在lca之上,对lca操作,return
如果L在lca之上,对lca左儿子操作,return
树剖[L,lca右儿子)
代码

#include<bits/stdc++.h> using namespace std; const int MAX=1e6+10; #define int long long int n,q,x,y,d,op,fa[MAX],son[MAX][2],rt,bro[MAX]; int cnt[MAX],s[MAX]; int Son[MAX],siz[MAX],dfn[MAX],chuo,top[MAX],dep[MAX],Fa[MAX][20]; vector<int>g[MAX]; struct node{ int sum,laz; } t[MAX<<1]; inline int read(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } void dfs(int,int); void Do(int,int,int); inline int getlca(int,int); void fhe(int,int); void che(int,int); inline void add(int,int,int,int); inline int ask(int,int,int); void update(int,int,int,int,int,int); int query(int,int,int,int,int); inline void pushdown(int,int,int); signed main(){ n=read();q=read(); for(int i=1+n;i<2*n;++i){ son[i][0]=read();son[i][1]=read(); bro[son[i][0]]=son[i][1];bro[son[i][1]]=son[i][0]; }for(int i=n+1;i<2*n;++i) if(!bro[i]){rt=i;break;} son[0][0]=son[0][1]=rt;bro[rt]=rt; dfs(rt,rt);Do(rt,0,0);Do(rt,0,1); fhe(rt,rt);che(rt,rt); for(int i=2;i<=chuo;++i) s[i]+=s[i-1]; while(q--){ op=read();x=read();y=read(); int lca=getlca(x,y);// if(x==son[Fa[x][0]][0]) x=bro[fa[x]]; if(y==son[Fa[y][0]][1]) y=bro[fa[y]]; if(op==1){ d=read(); if(dep[x]<=dep[lca]&&dep[y]<=dep[lca]){ update(1,1,2*n-1,dfn[lca],dfn[lca],d);continue; }add(x,lca,d,0);add(y,lca,d,1); }else{ if(dep[x]<=dep[lca]&&dep[y]<=dep[lca]){ printf("%lld\n",query(1,1,2*n-1,dfn[lca],dfn[lca])); continue; }printf("%lld\n",ask(x,lca,0)+ask(y,lca,1)); } } } void dfs(int u,int f){ dep[u]=dep[f]+1;Fa[u][0]=f; for(int i=1;(1<<i)<=dep[u];++i) Fa[u][i]=Fa[Fa[u][i-1]][i-1]; if(!son[u][0]){cnt[u]=1;return;} dfs(son[u][0],u);dfs(son[u][1],u); cnt[u]=cnt[son[u][0]]+cnt[son[u][1]]; }void Do(int u,int x,int p){ if(!u) return; if(u==son[Fa[u][0]][p]) g[son[x][p]].push_back(u); Do(son[u][p],x,p);Do(son[u][p^1],u,p); }inline int getlca(int x,int y){ if(dep[x]<dep[y]) swap(x,y); int h=dep[x]-dep[y]; for(int i=0;i<20;++i) if(h&(1<<i)) x=Fa[x][i]; if(x==y) return x; for(int i=19;i>=0;--i) if(Fa[x][i]!=Fa[y][i]){ x=Fa[x][i];y=Fa[y][i]; } return Fa[x][0]; } void fhe(int u,int f){ fa[u]=f;siz[u]=1; for(int i=0;i<g[u].size();++i){ int v=g[u][i]; fhe(v,u);siz[u]+=siz[v]; if(siz[Son[u]]<siz[v]) Son[u]=v; } }void che(int u,int ance){ dfn[u]=++chuo;top[u]=ance;s[chuo]=cnt[u]; if(Son[u]) che(Son[u],ance); for(int i=0;i<g[u].size();++i) if(g[u][i]!=Son[u]) che(g[u][i],g[u][i]); }inline void add(int x,int y,int num,int p){ if(dep[x]<=dep[y]){ update(1,1,2*n-1,dfn[son[y][p]],dfn[son[y][p]],num); return; }y=son[y][p^1]; while(top[x]!=top[y]){ update(1,1,2*n-1,dfn[top[x]],dfn[x],num); x=fa[top[x]]; }update(1,1,2*n-1,dfn[y]+1,dfn[x],num); }inline int ask(int x,int y,int p){ if(dep[x]<=dep[y]) return query(1,1,2*n-1,dfn[son[y][p]],dfn[son[y][p]]); y=son[y][p^1];int ans=0; while(top[x]!=top[y]){ ans+=query(1,1,2*n-1,dfn[top[x]],dfn[x]); x=fa[top[x]]; }ans+=query(1,1,2*n-1,dfn[y]+1,dfn[x]); return ans; } void update(int pos,int l,int r,int ll,int rr,int d){ if(l>=ll&&r<=rr){ t[pos].sum+=(s[r]-s[l-1])*d; t[pos].laz+=d;return;// }int mid=l+r>>1;pushdown(pos,s[mid]-s[l-1],s[r]-s[mid]); if(mid>=ll) update(pos<<1,l,mid,ll,rr,d); if(mid<rr) update(pos<<1|1,mid+1,r,ll,rr,d); t[pos].sum=t[pos<<1].sum+t[pos<<1|1].sum; }int query(int pos,int l,int r,int ll,int rr){ if(l>rr||r<ll) return 0; if(l>=ll&&r<=rr) return t[pos].sum; int mid=l+r>>1;pushdown(pos,s[mid]-s[l-1],s[r]-s[mid]); return query(pos<<1,l,mid,ll,rr)+query(pos<<1|1,mid+1,r,ll,rr); }inline void pushdown(int pos,int l1,int l2){ if(t[pos].laz){ t[pos<<1].sum+=l1*t[pos].laz;t[pos<<1|1].sum+=l2*t[pos].laz; t[pos<<1].laz+=t[pos].laz;t[pos<<1|1].laz+=t[pos].laz; t[pos].laz=0; } }
因为线段树懒标记没有+=,调了一下午,原地去世
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2022-07-23 KMP