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]);
}

 

posted @ 2020-08-21 19:36  Li_yxxx  阅读(70)  评论(0)    收藏  举报