牛客挑战赛40E 小V和gcd树(边权为俩点的gcd,求路径上小于等于k的边权数)
题:https://ac.nowcoder.com/acm/contest/5555/E
分析:树上路径,带修改,考虑树链剖分,对于操作1,把u的值改为x,影响的权值只是,u的父亲以及他孩子之间的边,那么我们就把重链的部分修改,其他的边不修改,即对u--son[u]和u--fa[u]的边的gcd修改即可(注意后者要保证在当前重链上才可修改);对于操作2,借助tp往上跳的过程中,由于重链上的边我们处理过了,所以这部分直接加,另外就是重链以为的部分边,这些边只可能是u--fa[u]之间的边,所以暴力统计即可
#include<cstdio> #include<iostream> #include<vector> #include<algorithm> using namespace std; #define pb push_back const int M=2e4+4; int fa[M],sz[M],deep[M],ans[M],dfn[M],tp[M],son[M],a[M]; int cnt; vector<int>g[M]; void dfs1(int u,int f){ fa[u]=f,sz[u]=1,deep[u]=deep[f]+1; for(auto v:g[u]){ if(v!=f){ dfs1(v,u); sz[u]+=sz[v]; if(sz[son[u]]<sz[v]) son[u]=v; } } } void dfs2(int u,int top){ tp[u]=top; dfn[u]=++cnt; if(u!=top) ans[dfn[u]]=__gcd(a[u],a[fa[u]]); if(son[u]) dfs2(son[u],top); for(auto v:g[u]){ if(v!=fa[u]&&v!=son[u]) dfs2(v,v); } } int solve(int u,int v,int k){ int res=0; while(tp[u]!=tp[v]){ if(deep[tp[u]]<deep[tp[v]]) swap(u,v); for(int i=dfn[tp[u]]+1;i<=dfn[u];i++) res+=(ans[i]<=k); res+=(__gcd(a[tp[u]],a[fa[tp[u]]])<=k); u=fa[tp[u]]; } if(deep[u]<deep[v]) swap(u,v); for(int i=dfn[v]+1;i<=dfn[u];i++) res+=(ans[i]<=k); return res; } int main(){ int n,q; scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int u,v,i=1;i<n;i++){ scanf("%d%d",&u,&v); g[u].pb(v),g[v].pb(u); } dfs1(1,0),dfs2(1,1); while(q--){ int op,u,v,x,k; scanf("%d",&op); if(op==1){ scanf("%d%d",&u,&x); a[u]=x; if(tp[u]!=u) ans[dfn[u]]=__gcd(a[u],a[fa[u]]); if(son[u]) ans[dfn[son[u]]]=__gcd(a[u],a[son[u]]); } else{ scanf("%d%d%d",&u,&v,&k); printf("%d\n",solve(u,v,k)); } } return 0; }