LCA_树上差分_洛谷_P1600天天爱跑步
题目链接:洛谷P1600 天天爱跑步
推荐一个写的很好很好很好*n的题解
思路见代码注释
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> using namespace std; int const N=300000; int f[2*N][40],deep[2*N],tot[2*N]; int lasta[2*N],t[2*N],dis[2*N],anss[2*N],lastl[2*N],lastt[2*N],l[2*N],r[2*N]; int b1[2*N],b2[2*N]; int n,m,numa,numl,numt,ans[2*N]; struct node{ int y,next; }a[2*N],lca[2*N],tn[2*N]; void adda(int x,int y) //储存树的边 { numa++; a[numa].y=y; a[numa].next=lasta[x]; lasta[x]=numa; } void addt(int x,int y)//记录以x为终点的玩家编号 { numt++; tn[numt].y=y; tn[numt].next=lastt[x]; lastt[x]=numt; } void addl(int x,int y)//记录起点和终点的lca为x的玩家编号 { numl++; lca[numl].y=y; lca[numl].next=lastl[x]; lastl[x]=numl; } void buildtree(int now)//lca预处理、记录深度deep、找到每个点i的父节点f[i][0] { for(int i=lasta[now];i;i=a[i].next) if(!deep[a[i].y]) { f[a[i].y][0]=now; deep[a[i].y]=deep[now]+1; buildtree(a[i].y); } } int getlca(int a,int b)//倍增求a和b的lca { if(deep[a]>deep[b]) swap(a,b); for(int i=29;i>=0;i--) if(deep[f[b][i]]>=deep[a]) b=f[b][i]; if(a==b) return a; for(int i=30;i>=0;i--) if(f[a][i]!=f[b][i]) { a=f[a][i]; b=f[b][i]; } return f[a][0]; } void dfs(int root)//深搜得到答案 { int t1=b1[deep[root]+t[root]]; //记录最初上行的贡献值b1 int t2=b2[t[root]-deep[root]+N]; //记录最初下行的贡献值b2 for(int i=lasta[root];i;i=a[i].next) //遍历 if(a[i].y!=f[root][0]) dfs(a[i].y); b1[deep[root]]+=tot[root]; //加上自身作为起点,上行时对答案的贡献 for(int i=lastt[root];i;i=tn[i].next) //加上自身作为终点,下行时对答案的贡献 { b2[dis[tn[i].y]-deep[r[tn[i].y]]+N]++; } ans[root]+=b1[deep[root]+t[root]]-t1+b2[t[root]-deep[root]+N]-t2; //取差值得到以root为根的整颗子树的贡献 for(int i=lastl[root];i;i=lca[i].next) //将root作为lca的起点和终点的贡献减去 { //对于一个点,在以它为根的子树中,不经过它自身的路径的贡献与它无关 b1[deep[l[lca[i].y]]]--; b2[dis[lca[i].y]-deep[r[lca[i].y]]+N]--; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n-1;i++) { int x,y; scanf("%d%d",&x,&y); adda(x,y); adda(y,x); } f[1][0]=1; deep[1]=1; buildtree(1); for(int i=1;i<=30;i++) //lca初始化 for(int now=1;now<=n;now++) f[now][i]=f[f[now][i-1]][i-1]; for(int i=1;i<=n;i++) scanf("%d",&t[i]); for(int i=1;i<=m;i++) { scanf("%d%d",&l[i],&r[i]); int lca=getlca(l[i],r[i]); dis[i]=deep[r[i]]+deep[l[i]]-2*deep[lca]; tot[l[i]]++; //记录以l[i]为起点的路径的数量 addt(r[i],i); //将以r[i]为终点的路径记录下来 addl(lca,i); //将起点和终点以点lca为lca的路径记录下来 if(deep[lca]+t[lca]==deep[l[i]]) ans[lca]--; //如果lca为起点或者终点,上行和下行都会被记录一次,所以要减去重复的一次 } dfs(1); for(int i=1;i<=n;i++) printf("%d ",ans[i]); }

浙公网安备 33010602011771号