题意:给你以1为根的树,问u到v有多少个节点,权值严格大于路径上所有节点的权值和w。保证v在u到根节点的路径上。
首先我们考虑普通的解法,对于每一次询问,dfs从u节点到v节点,保存前缀最大值,那么当搜到的这个节点的权值大于前缀最大值时,答案加一,这样做的时间复杂度是o(nq)
code:
#include <bits/stdc++.h> #define pb push_back using namespace std; const int maxn = 1e5+100; vector<int>G[maxn]; int f[maxn],a[maxn]; void dfs(int u,int fa) { f[u] = fa; for(int i=0;i<G[u].size();i++) { int v = G[u][i]; if(v == fa)continue; dfs(v,u); } } void qu(int u,int v,int w) { int mx = w; int ans = 0; while(u != f[v]) { if(a[u] > mx)++ans; mx = max(mx,a[u]); u = f[u]; } printf("%d\n",ans); } int main() { int n,q; scanf("%d%d",&n,&q); for(int i=1;i<=n;i++)scanf("%d",a+i); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); G[u].pb(v); G[v].pb(u); } dfs(1,0); while(q--) { int u,v,w; scanf("%d%d%d",&u,&v,&w); qu(u,v,w); } }
如何优化?倍增。f[i][j]代表的是从i节点往上严格比她大的第2^j个元素的编号,那么f[u][i] = f[f[u][i-1]][i-1]。关键是如何求出f[u][0]?因为f[fa][0-logn]都先于u求出来了,那么如果a[fa] > a[u]的话,f[u][0] = fa;否则就以fa为起点倍增求出第一个大于a[u]的元素编号赋给f[u][0],这样的时间复杂度时o(qlogn)。
code:
#include <bits/stdc++.h> #define pb push_back using namespace std; const int maxn = 2e5+100; int a[maxn],d[maxn]; vector<int>G[maxn]; int f[maxn][20],dep[maxn]; void dfs(int u,int fa,int ds) { dep[u] = ds; int tt = fa; if(a[fa] > a[u]) f[u][0] = fa; else { for(int i=19;~i;i--) if(f[fa][i] && a[f[fa][i]] <= a[u])fa = f[fa][i]; f[u][0] = f[fa][0]; } for(int i=1;i<20;i++)f[u][i] = f[f[u][i-1]][i-1]; for(int i=0;i<G[u].size();i++) { if(G[u][i] == tt)continue; dfs(G[u][i],u,ds+1); } } int main() { int n,q; scanf("%d%d",&n,&q); for(int i=1;i<=n;i++)scanf("%d",a+i); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); G[u].pb(v); G[v].pb(u); } for(int i=1;i<=q;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); G[i+n].pb(u); G[u].pb(i+n); d[i] = v,a[i+n] = w; } dfs(1,0,1); for(int i=1;i<=q;i++) { int u = n+i,v = d[i],ans=0; for(int i=19;~i;i--) if(dep[f[u][i]] >= dep[v])u = f[u][i],ans+=1<<i; printf("%d\n",ans); } }