树上问题三题
例1
有一棵树共
首先考虑暴力方法:
- 枚举树上所有
个节点,判断是否有节点同时在两个路径上。复杂度为 - 枚举路径
上每一个节点 ,判断 是否在路径 上。平均复杂度为 ,最差复杂度为
还能不能更快?
手算几个样例,可以发现规律:设
因此产生了正解思路:对于路径
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll ans; int L,n,q,d[200009],p[200009][209],tI[200009],tO[200009],timer; vector<int> to[200009]; void depth(int x,int dep){ d[x]=dep; for(int i=0;i<to[x].size();i++){ if(d[to[x][i]]) continue; depth(to[x][i],dep+1); } } void init(int u,int fa){ tI[u]=++timer; p[u][0]=fa; for(int i=1;i<=L;i++) p[u][i]=p[p[u][i-1]][i-1]; for(int i=0;i<to[u].size();i++){ if(to[u][i]!=fa) init(to[u][i],u); } tO[u]=++timer; } bool up(int u,int v){ return !u||tI[u]<=tI[v]&&tO[v]<=tO[u]; } int lca(int u,int v){ if(u==v) return u; if(up(u,v)) return u; if(up(v,u)) return v; for(int i=L;i>=0;i--){ if(!up(p[u][i],v)) u=p[u][i]; } return p[u][0]; } int dst(int x,int y){ return d[x]+d[y]-2*d[lca(x,y)]; } bool onPath(int x,int u,int v){ return dst(u,x)+dst(v,x)==dst(u,v); } int main(){ cin>>n; for(int i=1;i<=n-1;i++){ int u,v; cin>>u>>v; to[u].push_back(v); to[v].push_back(u); } depth(1,1);init(1,0); L=log(n)/log(2)+1; cin>>q; for(int i=1;i<=q;i++){ int a,b,u,v;cin>>u>>v>>a>>b; int uv=lca(u,v),ab=lca(a,b); if(d[ab]>d[uv]) ans+=onPath(ab,u,v); else ans+=onPath(uv,a,b); } cout<<ans; return 0; }
例2
输入一棵树,输出该树的直径。
思路1:
枚举每一个转折点,计算以该点为转折点的两条最长路(注意不能有重叠的边),输出最大值。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; int ans,n,lst[100009],h1[100009],h2[100009]; vector<int> to[100009]; void dfs(int u,int fa){ for(int i=0;i<to[u].size();i++){ int v=to[u][i]; if(v==fa) continue; dfs(v,u); h2[u]=max(h2[u],h1[v]+1); if(h2[u]>h1[u]) swap(h2[u],h1[u]); } return ; } void solve(){ dfs(1,0); for(int i=1;i<=n;i++) lst[i]=h1[i]+h2[i]; ans=*max_element(lst+1,lst+1+n); return ; } int main(){ cin>>n; for(int i=1;i<=n-1;i++){ int u,v; cin>>u>>v; to[u].push_back(v); to[v].push_back(u); } solve(); cout<<ans; return 0; }
思路2:
直径的
例3
春节期间,小明要去长辈家里拜年。假设小明和爸妈住在
思路1:枚举转折点,预计算前 长路径
思路2:计算直径 枚举
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll n,d[200009],tI[200009],tO[200009],p[200009][109],timer,L; vector<ll> to[200009]; vector<ll> dis[200009]; void dfs(ll x,ll dep){ d[x]=dep; for(ll i=0;i<to[x].size();i++){ ll y=to[x][i]; if(d[y]) continue; dfs(y,dep+dis[x][i]); } return ; } void init(ll u,ll fa){ tI[u]=++timer; p[u][0]=fa; for(ll i=1;i<=L;i++) p[u][i]=p[p[u][i-1]][i-1]; for(ll i=0;i<to[u].size();i++){ if(to[u][i]!=fa) init(to[u][i],u); } tO[u]=++timer; } bool up(ll u,ll v){ return !u||tI[u]<=tI[v]&&tO[v]<=tO[u]; } ll lca(ll u,ll v){ if(u==v) return u; if(up(u,v)) return u; if(up(v,u)) return v; for(ll i=L;i>=0;i--){ if(!up(p[u][i],v)) u=p[u][i]; } return p[u][0]; } ll dst(ll x,ll y){ return d[x]+d[y]-2*d[lca(x,y)]; } int main(){ scanf("%lld",&n); for(ll i=1;i<=n-1;i++){ ll u,v,w; scanf("%lld%lld%lld",&u,&v,&w); to[u].push_back(v); to[v].push_back(u); dis[u].push_back(w); dis[v].push_back(w); } dfs(1,1); ll B=max_element(d+1,d+1+n)-d; memset(d,0,sizeof(d)); dfs(B,1); ll C=max_element(d+1,d+1+n)-d; ll ans=0; memset(d,0,sizeof(d)); dfs(1,1); L=log(n)/log(2)+1; init(1,0); for(ll A=1;A<=n;A++){ ll AB=dst(A,B); ll AC=dst(A,C); ll cost=min(AB,AC)+dst(B,C); ans=max(ans,cost); } printf("%lld",ans); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】