树上差分

  托了好久的树上差分,感觉托了一个世纪

  顾z大佬写的关于树上差分的文章洛谷科技,可以直接看这个,下面的还是我自己的个人理解。

  差分是种思想,不管是在1维,2维还是在树上都一样的,就是在影响开始和结束的地方设置影响值进行更新,就是实现方法上的问题。

  最基本的两种树上差分是点差分和,边差分。因为有些时候给出的不是点的信息,而是边的信息,而建成树的话边的信息可以,然后对于一条边的信息,我们可以把它保存在子节点上,处理上就和点差分有所不同。

  首先点差分,我们要知道树上一个类似前缀和的消息,就是一个节点可以保存在它子树上的消息,类似下图的sum,就是树上关于val的前缀和。而就是先对子数进行遍历。

  那么假设我们现在要对3到4路径上的节点的权值都+a,因为我们是先遍历的子树,那么影响的起点就是3和4,我们在3和4设置个+a,而结束的地方呢,3和4遍历后,那么2那里会有+2a的影响,而实际2应该只+a,所以2处再设置个-a,这时对上面的1还有+a的影响,1处还得再-a。

  总的来说就是如果路径的起点是u,终点是v,那么dif[u]+=a,dif[v]+=a,dif[lca(u,v)]-=a,dif[fa[lca(u,v)]]-=a,其中lca(u,v)就是u和v的最近公共祖先,fa[lca(u,v)]是lca(u,v)的父节点

  所以,树上差分一个很关键的点就是要懂求lca,这个在我前面的随笔我有写到,感兴趣的可以去看看。

  而边差分类似,不同的在于边信息的储存在子节点,所以lca处的影响应该是0,那么就是dif[u]+=a,dif[v]+=a,dif[lca(u,v)]-=2*a

P3128最大流

  就是简单的点差分,lca上我用的树上倍增

#include<cstdio>
const int N=50118,fn=20;
struct Side{
    int v,ne;
}S[N<<1];
int sn,ans,head[N],dep[N],dif[N],fa[N][25];
void init(int n)
{
    sn=0;
    for(int i=0;i<=n;i++)
    {
        head[i]=-1;
        dep[i]=0;
        dif[i]=0;
        for(int j=0;j<=fn;j++)
            fa[i][j]=0;
    }
}
void add(int u,int v)
{
    S[sn].v=v;
    S[sn].ne=head[u];
    head[u]=sn++;
}
void dfs1(int u)
{
    for(int i=1;i<=fn;i++)
    {
        int f=fa[u][i-1];
        if(!f)
            break;
        fa[u][i]=fa[f][i-1];
    }
    for(int i=head[u];i!=-1;i=S[i].ne)
    {
        int v=S[i].v;
        if(v!=fa[u][0])
        {
            fa[v][0]=u;
            dep[v]=dep[u]+1;
            dfs1(v);
        }
    }
}
int lca(int u,int v)
{
    if(dep[u]<dep[v]){
        int temp=u;u=v;v=temp;
    }
    int disd=dep[u]-dep[v];
    for(int i=0;i<=fn;i++)
        if((1<<i)&disd)
            u=fa[u][i];
    if(u==v)
        return u;
    for(int i=fn;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
        {
            u=fa[u][i];
            v=fa[v][i];
        }
    return fa[u][0];
}
void dfs2(int u)//跑一遍深搜遍历,处理差分数组 
{
    for(int i=head[u];i!=-1;i=S[i].ne)
    {
        int v=S[i].v;
        if(v!=fa[u][0])
        {
            dfs2(v);
            dif[u]+=dif[v];
        }
    }
    if(dif[u]>ans)
        ans=dif[u];
}
int main()
{
    int n,m,u,v;
    while(~scanf("%d%d",&n,&m))
    {
        init(n);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs1(1);
        while(m--)
        {
            scanf("%d%d",&u,&v);
            int l=lca(u,v);
            dif[u]++,dif[v]++;
            dif[l]--,dif[fa[l][0]]--;
        }
        ans=0;
        dfs2(1);
        printf("%d\n",ans);
    }
    return 0;
} 
挂羊头卖狗肉

P3258松鼠的新家

  也是点差分,像a1->a2->a3->a4,可以分成a1->a2,a2->a3,a3->a4三条路,而像a2在a1->a2这条路径已经计算过了,a2->a3上a2就重复计算了,而最后的a4外也不用算了,所以除了a1其他节点答案都要-1

  1 #include<cstdio>
  2 const int N=300118,fn=20;
  3 struct Side{
  4     int v,ne;
  5 }S[N<<1];
  6 int sn,ans,a[N],head[N],dep[N],dif[N],fa[N][25];
  7 void init(int n)
  8 {
  9     sn=0;
 10     for(int i=0;i<=n;i++)
 11     {
 12         head[i]=-1;
 13         dep[i]=0;
 14         dif[i]=0;
 15         for(int j=0;j<=fn;j++)
 16             fa[i][j]=0;
 17     }
 18 }
 19 void add(int u,int v)
 20 {
 21     S[sn].v=v;
 22     S[sn].ne=head[u];
 23     head[u]=sn++;
 24 }
 25 void dfs1(int u)
 26 {
 27     for(int i=1;i<=fn;i++)
 28     {
 29         int f=fa[u][i-1];
 30         if(!f)
 31             break;
 32         fa[u][i]=fa[f][i-1];
 33     }
 34     for(int i=head[u];i!=-1;i=S[i].ne)
 35     {
 36         int v=S[i].v;
 37         if(v!=fa[u][0])
 38         {
 39             fa[v][0]=u;
 40             dep[v]=dep[u]+1;
 41             dfs1(v);
 42         }
 43     }
 44 }
 45 int lca(int u,int v)
 46 {
 47     if(dep[u]<dep[v]){
 48         int temp=u;u=v;v=temp;
 49     }
 50     int disd=dep[u]-dep[v];
 51     for(int i=0;i<=fn;i++)
 52         if((1<<i)&disd)
 53             u=fa[u][i];
 54     if(u==v)
 55         return u;
 56     for(int i=fn;i>=0;i--)
 57         if(fa[u][i]!=fa[v][i])
 58         {
 59             u=fa[u][i];
 60             v=fa[v][i];
 61         }
 62     return fa[u][0];
 63 }
 64 void dfs2(int u)
 65 {
 66     for(int i=head[u];i!=-1;i=S[i].ne)
 67     {
 68         int v=S[i].v;
 69         if(v!=fa[u][0])
 70         {
 71             dfs2(v);
 72             dif[u]+=dif[v];
 73         }
 74     }
 75     if(dif[u]>ans)
 76         ans=dif[u];
 77 }
 78 int main()
 79 {
 80     int n,m,u,v;
 81     while(~scanf("%d",&n))
 82     {
 83         init(n);
 84         for(int i=1;i<=n;i++)
 85             scanf("%d",&a[i]);
 86         for(int i=1;i<n;i++)
 87         {
 88             scanf("%d%d",&u,&v);
 89             add(u,v);
 90             add(v,u);
 91         }
 92         dfs1(a[1]);
 93         for(int i=2;i<=n;i++)
 94         {
 95             int l=lca(a[i-1],a[i]);
 96             dif[a[i-1]]++,dif[a[i]]++;
 97             dif[l]--,dif[fa[l][0]]--;
 98         }
 99         dfs2(a[1]);
100         for(int i=1;i<=n;i++)
101         {
102             if(i==a[1])
103                 printf("%d\n",dif[i]);
104             else//除a1外,其他起点多跑了一次,而且终点最后一次也不算 
105                 printf("%d\n",dif[i]-1);
106         }
107     }
108     return 0;
109 } 
放糖果

P2680运输计划

  我们可以贪心地想到找到最大那条路径后,然后去掉最长边,与第二长的路径比较。但是这样很容易有反例,就是第一长的路径,第二长条的路径有一条公共的边,去掉这条边才是答案最小。那我们可以二分答案,对于当前二分的这个答案ans,那么这个ans合理的话,对于大于ans的那些路径就应该有一条公共的最长边,然后去掉它可以使得最长的路径小于等于ans。然后求lca,tarjan是O(n+m),树上倍增是nlogn,用树上倍增有一个点会超时,这时还有个小优化,就是让二分的左端点是最长的路径减去最长的边。

  1 #include<cstdio>
  2 #include<algorithm>
  3 using namespace std;
  4 const int N=300118,fn=20;
  5 struct Side{
  6     int v,w,ne; 
  7 }S[N<<1];
  8 struct Query{
  9     int u,v,lca,len;
 10     Query(){}
 11     Query(int len):len(len){}
 12     bool operator<(const Query &q1) const{
 13         return len<q1.len;
 14     }
 15 }q[N];
 16 int n,m,sn,head[N],val[N],dep[N],dis[N],dif[N],fa[N][25];
 17 void init(int n)
 18 {
 19     sn=0;
 20     for(int i=0;i<=n;i++)
 21     {
 22         head[i]=-1;
 23         dep[i]=0;
 24         dif[i]=0;
 25         val[i]=0;
 26         dis[i]=0;
 27         for(int j=0;j<=fn;j++)
 28             fa[i][j]=0;
 29     }
 30 }
 31 void add(int u,int v,int w)
 32 {
 33     S[sn].v=v;
 34     S[sn].w=w;
 35     S[sn].ne=head[u];
 36     head[u]=sn++;
 37 }
 38 void dfs1(int u)
 39 {
 40     for(int i=1;i<=fn;i++)
 41     {
 42         int f=fa[u][i-1];
 43         if(!f)
 44             break;
 45         fa[u][i]=fa[f][i-1];
 46     }
 47     for(int i=head[u];i!=-1;i=S[i].ne)
 48     {
 49         int v=S[i].v;
 50         if(v!=fa[u][0])
 51         {
 52             fa[v][0]=u;
 53             val[v]=S[i].w;//边的权值保存在子节点上 
 54             dep[v]=dep[u]+1;
 55             dis[v]=dis[u]+S[i].w;    
 56             dfs1(v);
 57         }
 58     }
 59 }
 60 int Lca(int u,int v)
 61 {
 62     if(dep[u]<dep[v]){
 63         int temp=u;u=v;v=temp;
 64     }
 65     int disd=dep[u]-dep[v];
 66     for(int i=0;i<=fn;i++)
 67         if((1<<i)&disd)
 68             u=fa[u][i];
 69     if(u==v)
 70         return u;
 71     for(int i=fn;i>=0;i--)
 72         if(fa[u][i]!=fa[v][i])
 73         {
 74             u=fa[u][i];
 75             v=fa[v][i];
 76         }
 77     return fa[u][0];
 78 }
 79 void dfs2(int u)
 80 {
 81     for(int i=head[u];i!=-1;i=S[i].ne)
 82     {
 83         int v=S[i].v;
 84         if(v!=fa[u][0])
 85         {
 86             dfs2(v);
 87             dif[u]+=dif[v];
 88         }
 89     }
 90 }
 91 bool check(int lim)
 92 {
 93     int p=upper_bound(q+1,q+1+m,Query(lim))-q;//找到第一个大于等于lim的位置 
 94     if(m-p+1<=0)
 95         return 1;
 96     for(int i=p;i<=m;i++)
 97     {
 98         dif[q[i].u]++,dif[q[i].v]++;
 99         dif[q[i].lca]-=2;
100     }//对经过的边进行差分处理 
101     dfs2(1);
102     int maxl=0;
103     for(int i=1;i<=n;i++)
104     {
105         if(dif[i]==m-p+1)
106             maxl=max(maxl,val[i]);//在都经过的边取一个最大值 
107         dif[i]=0;//每次要把差分数组清空,为下次做准备 
108     }
109     return q[m].len-maxl<=lim;
110 }
111 int main()
112 {
113     int u,v,w;
114     while(~scanf("%d%d",&n,&m))
115     {
116         init(n);
117         int maxw=0;
118         for(int i=1;i<n;i++)
119         {
120             scanf("%d%d%d",&u,&v,&w);
121             maxw=max(w,maxw);
122             add(u,v,w);
123             add(v,u,w);
124         }
125         dfs1(1);
126         int l=0,r=0,ans=0;
127         for(int i=1;i<=m;i++)
128         {
129             scanf("%d%d",&q[i].u,&q[i].v);
130             q[i].lca=Lca(q[i].u,q[i].v);
131             q[i].len=dis[q[i].u]+dis[q[i].v]-2*dis[q[i].lca];
132             l=min(l,q[i].len);
133             r=max(r,q[i].len);
134         }
135         l=r-maxw;
136         sort(q+1,q+1+m);
137         while(l<=r)
138         {
139             int mid=(l+r)>>1;
140             if(check(mid))
141                 ans=mid,r=mid-1;
142             else
143                 l=mid+1;
144         }
145         printf("%d\n",ans);
146     }
147     return 0;
148 }
倍增大法好
  1 #include<cstdio>
  2 #include<vector>
  3 #include<algorithm>
  4 using namespace std;
  5 typedef pair<int,int> pii;
  6 const int N=300118;
  7 struct Plan{
  8     int u,v,lca,len;
  9     Plan(){}
 10     Plan(int len):len(len){}
 11     bool operator<(const Plan &p1) const{
 12         return len<p1.len;
 13     }
 14 }p[N];
 15 struct Side{
 16     int v,w,ne; 
 17 }s[N<<1],q[N<<1];
 18 int n,m,l,r,sn,qn,heads[N],headq[N],fa[N],dis[N],cost[N],vis[N],dif[N];
 19 void init()
 20 {
 21     l=r=0;
 22     sn=qn=0;
 23     for(int i=0;i<=n;i++)
 24     {
 25         fa[i]=i;
 26         vis[i]=0;
 27         dif[i]=0;
 28         heads[i]=headq[i]=-1;
 29     }
 30 }
 31 void adds(int u,int v,int w)
 32 {
 33     s[sn].v=v;
 34     s[sn].w=w;
 35     s[sn].ne=heads[u];
 36     heads[u]=sn++;
 37 }
 38 void addq(int u,int v,int w)
 39 {
 40     q[qn].v=v;
 41     q[qn].w=w;
 42     q[qn].ne=headq[u];
 43     headq[u]=qn++;
 44 }
 45 int gui(int x){
 46     return fa[x]==x ? x : fa[x]=gui(fa[x]);
 47 }
 48 void tarjan(int u,int f)
 49 {
 50     for(int i=heads[u];i!=-1;i=s[i].ne)
 51     {
 52         int v=s[i].v,w=s[i].w;
 53         if(v!=f)
 54         {
 55             cost[v]=w;
 56             dis[v]=dis[u]+w;
 57             tarjan(v,u);
 58             fa[v]=u;
 59             vis[v]=1;
 60         }
 61     }
 62     for(int i=headq[u];i!=-1;i=q[i].ne)
 63     {
 64         int v=q[i].v,id=q[i].w;
 65         if(vis[v])
 66         {
 67             p[id].u=u,p[id].v=v,p[id].lca=gui(v);
 68             p[id].len=dis[u]+dis[v]-2*dis[gui(v)];
 69             r=max(r,p[id].len);
 70         }
 71     }
 72 }
 73 void dfs(int u,int f)
 74 {
 75     for(int i=heads[u];i!=-1;i=s[i].ne)
 76     {
 77         int v=s[i].v;
 78         if(v!=f)
 79         {
 80             dfs(v,u);
 81             dif[u]+=dif[v];
 82         }
 83     }
 84 }
 85 bool check(int lim)
 86 {
 87     int pos=upper_bound(p+1,p+1+m,Plan(lim))-p;
 88     int num=m-pos+1;
 89     if(num<=0)
 90         return 1;
 91     for(int i=pos;i<=m;i++)
 92     {
 93         dif[p[i].u]++,dif[p[i].v]++;
 94         dif[p[i].lca]-=2;
 95     }
 96     dfs(1,0);
 97     int maxl=0;
 98     for(int i=1;i<=n;i++)
 99     {
100         if(dif[i]==num)
101             maxl=max(maxl,cost[i]);
102         dif[i]=0;
103     }
104     return p[m].len-maxl<=lim;
105 }
106 int main()
107 {
108     int u,v,w;
109     while(~scanf("%d%d",&n,&m))
110     {
111         init();
112         for(int i=1;i<n;i++)
113         {
114             scanf("%d%d%d",&u,&v,&w);
115             l=max(l,w);
116             adds(u,v,w);
117             adds(v,u,w);
118         }
119         for(int i=1;i<=m;i++)
120         {
121             scanf("%d%d",&u,&v);
122             addq(u,v,i);
123             addq(v,u,i);
124         }
125         tarjan(1,0);
126         sort(p+1,p+1+m);
127         int ans=0;
128         l=r-l;
129         while(l<=r)
130         {
131             int mid=(l+r)>>1;
132             if(check(mid))
133                 ans=mid,r=mid-1;
134             else
135                 l=mid+1;
136         }
137         printf("%d\n",ans);
138     }
139     return 0;
140 }
tanrja太牛了

P1600天天爱跑步

   这题不是点差分和边差分,而是一个差分思想。对于一个玩家的路径u和v,那么有影响的肯定只是路径上的点,假如有个点x在路径上,那么它能观察到的话这个玩家的话,如果起点u在x的子树上,那么应该满足dep[x]+w[x]=dep[u],其中dep代表的是深度,如果u不在x的子树上,那么v在x的子树上,我们通过v可以得到对应这条路径的u,然后应该满足w[x]=dep[u]-dep[lca(u,v)]+dep[x]-dep[lca(u,v)],那么就是w[x]-dep[x]=dep[u]-2*dep[lca(u,v)],那么我们就是把它分成u->lca,v->lca两条路上的影响

  其中dep[x]+w[x]和w[x]-dep[x]都是定值,那么我们可以记录一个关于深度的全局差分值,这样每条路径的影响的话,如果x是a条路径的起点,那么dif[dep[x]]+=a,而如果x是一条路径的终点的话,那么dif[dep[u]-2*dep[lca(u,v)]]++,需要注意的是dep[u]-2*dep[lca(u,v)]可能是负值,而且就算它是正值它代表的是u不在x的子树上,所以要加个大正整数M作为偏移值,处理负值以及dep[u]-2*dep[lca(u,v)]可能和dep[x]相同的情况

  然后答案的统计,我们要使得做出贡献的u和v在当前x的子树上,那么就是在进入x前先记录当前的dif值,然后遍历完子树后求个差异值就是答案。

  最后就是消除影响,我们可以想到因为是先遍历子树,那么最后影响结束的地方就是在lca处,那个如果x是一条路径的lca,那么我们得把这条路径贡献的影响去掉,

  思路便是如此,不明白的可以看代码注释或者去看一下网上的其他题解,我之前看见个带图的讲解,不过链接忘了。实现上的话,我用tarjan卡了两天的75分,结果用树上倍增一发过了,然后发现是对于u和v相同的路径,我这个tarjan写法的lca得特判一下。

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 using namespace std;
  5 const int N=301108,M=600000;
  6 struct Side{
  7     int v,ne,id;
  8 }s[N<<1],q[N<<1],pathv[N],patha[N];
  9 struct Player{
 10     int u,v,lca;
 11 }p[N]; 
 12 int sn,qn,vn,an,heads[N],headq[N],headv[N],heada[N],pathu[N];
 13 int n,m,ww[N],dep[N],ans[N],vis[N],fa[N],dif[M<<1];
 14 void init()
 15 {
 16     sn=qn=vn=an=dep[1]=0;
 17     memset(dif,0,sizeof(dif));
 18     for(int i=0;i<=n;i++)
 19     {
 20         fa[i]=i;
 21         ans[i]=0;
 22         vis[i]=0;
 23         pathu[i]=0;
 24         heads[i]=headq[i]=headv[i]=heada[i]=-1;
 25     }
 26 }
 27 void add(Side *S,int *head,int &Sn,int u,int v,int id)
 28 {
 29     S[Sn].v=v;
 30     S[Sn].id=id;
 31     S[Sn].ne=head[u];
 32     head[u]=Sn++;
 33 }
 34 int gui(int x){
 35     return fa[x]==x ? x : fa[x]=gui(fa[x]);
 36 }
 37 void tarjan(int u,int f)
 38 {
 39     for(int i=heads[u];~i;i=s[i].ne)
 40     {
 41         int v=s[i].v;
 42         if(v!=f)
 43         {
 44             dep[v]=dep[u]+1;
 45             tarjan(v,u);
 46             fa[v]=u;
 47             vis[v]=1;
 48         }
 49     }
 50     for(int i=headq[u];~i;i=q[i].ne)
 51     {
 52         int v=q[i].v,id=q[i].id;
 53         if(vis[v])
 54             p[id].lca=gui(v);
 55     }
 56 }
 57 void dfs(int u,int f)
 58 {
 59     int u_a=dep[u]+ww[u],v_a=ww[u]-dep[u]+M;
 60     int difu=dif[u_a],difv=dif[v_a];//先把子树外的dif记录 
 61     for(int i=heads[u];~i;i=s[i].ne)
 62     {
 63         int v=s[i].v;
 64         if(v!=f)
 65             dfs(v,u);
 66     }
 67     dif[dep[u]]+=pathu[u];//u到lca路径上的点的贡献 
 68     for(int i=headv[u];~i;i=pathv[i].ne)
 69     {
 70         int id=pathv[i].id;
 71         dif[dep[p[id].u]-2*dep[p[id].lca]+M]++;//v到lca路径上的点的贡献 
 72     }
 73     ans[u]+=dif[u_a]-difu+dif[v_a]-difv;
 74     for(int i=heada[u];~i;i=patha[i].ne)//消除贡献
 75     {
 76         int id=patha[i].id;
 77         dif[dep[p[id].u]]--;
 78         dif[dep[p[id].u]-2*dep[p[id].lca]+M]--;
 79     }
 80 }
 81 int main()
 82 {
 83     int u,v;
 84     while(~scanf("%d%d",&n,&m))
 85     {
 86         init(); 
 87         for(int i=1;i<n;i++)
 88         {
 89             scanf("%d%d",&u,&v);
 90             add(s,heads,sn,u,v,0);
 91             add(s,heads,sn,v,u,0);
 92         }
 93         for(int i=1;i<=n;i++)
 94             scanf("%d",&ww[i]);
 95         for(int i=1;i<=m;i++)
 96         {
 97             scanf("%d%d",&u,&v);
 98             p[i].u=u,p[i].v=v;
 99             if(u==v)//起点终点相同特判一下 
100             {
101                 p[i].lca=u;
102                 continue;
103             }
104             add(q,headq,qn,u,v,i);
105             add(q,headq,qn,v,u,i);
106         }
107         tarjan(1,0);
108         for(int i=1;i<=m;i++)
109         {
110             pathu[p[i].u]++;//记录作为起点的路径
111             //因为起点的影响就是dep[u],所以无需建路径 
112             add(pathv,headv,vn,p[i].v,p[i].u,i);
113             //记录作为终点的路径 
114             add(patha,heada,an,p[i].lca,p[i].u,i);
115             //记录作为lca的路径 
116             if(dep[p[i].lca]+ww[p[i].lca]==dep[p[i].u])
117                 ans[p[i].lca]--;
118             //如果u对lca有贡献,那么v时也会统计该贡献,多算了一个,去掉 
119         }
120         dfs(1,0);
121         for(int i=1;i<=n;i++)
122         {
123             if(i>1)
124                 putchar(' ');
125             printf("%d",ans[i]);
126         }
127         putchar('\n');
128     }
129     return 0;
130 }
tarjan太强了
  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 using namespace std;
  5 const int N=301108,M=600000,fn=20;
  6 struct Side{
  7     int v,ne,id;
  8 }s[N<<1],q[N<<1],pathv[N],patha[N];
  9 struct Player{
 10     int u,v,lca;
 11 }p[N]; 
 12 int sn,vn,an,heads[N],headv[N],heada[N],pathu[N];
 13 int n,m,ww[N],dep[N],ans[N],dif[M<<1],fa[N][25];;
 14 void init()
 15 {
 16     sn=vn=an=dep[1]=0;
 17     memset(dif,0,sizeof(dif));
 18     for(int i=0;i<=n;i++)
 19     {
 20         ans[i]=0;
 21         pathu[i]=0;
 22         heads[i]=headv[i]=heada[i]=-1;
 23         for(int j=0;j<=fn;j++)
 24             fa[i][j]=0;
 25     }
 26 }
 27 void add(Side *S,int *head,int &Sn,int u,int v,int id)
 28 {
 29     S[Sn].v=v;
 30     S[Sn].id=id;
 31     S[Sn].ne=head[u];
 32     head[u]=Sn++;
 33 }
 34 void dfs1(int u)
 35 {
 36     for(int i=1;i<=fn;i++)
 37     {
 38         int f=fa[u][i-1];
 39         if(!f)
 40             break;
 41         fa[u][i]=fa[f][i-1];
 42     }
 43     for(int i=heads[u];~i;i=s[i].ne)
 44     {
 45         int v=s[i].v;
 46         if(v!=fa[u][0])
 47         {
 48             fa[v][0]=u;
 49             dep[v]=dep[u]+1;    
 50             dfs1(v);
 51         }
 52     }
 53 }
 54 int getlca(int u,int v)
 55 {
 56     if(dep[u]<dep[v]){
 57         int temp=u;u=v;v=temp;
 58     }
 59     int disd=dep[u]-dep[v];
 60     for(int i=0;i<=fn;i++)
 61         if((1<<i)&disd)
 62             u=fa[u][i];
 63     if(u==v)
 64         return u;
 65     for(int i=fn;i>=0;i--)
 66         if(fa[u][i]!=fa[v][i])
 67         {
 68             u=fa[u][i];
 69             v=fa[v][i];
 70         }
 71     return fa[u][0];
 72 }
 73 void dfs(int u,int f)
 74 {
 75     int u_a=dep[u]+ww[u],v_a=ww[u]-dep[u]+M;
 76     int difu=dif[u_a],difv=dif[v_a];
 77     for(int i=heads[u];~i;i=s[i].ne)
 78     {
 79         int v=s[i].v;
 80         if(v!=f)
 81             dfs(v,u);
 82     }
 83     dif[dep[u]]+=pathu[u];
 84     for(int i=headv[u];~i;i=pathv[i].ne)
 85     {
 86         int id=pathv[i].id;
 87         dif[dep[p[id].u]-2*dep[p[id].lca]+M]++;
 88     }
 89     ans[u]+=dif[u_a]-difu+dif[v_a]-difv;
 90     for(int i=heada[u];~i;i=patha[i].ne)
 91     {
 92         int id=patha[i].id;
 93         dif[dep[p[id].u]]--;
 94         dif[dep[p[id].u]-2*dep[p[id].lca]+M]--;
 95     }
 96 }
 97 int main()
 98 {
 99     int u,v;
100     while(~scanf("%d%d",&n,&m))
101     {
102         init(); 
103         for(int i=1;i<n;i++)
104         {
105             scanf("%d%d",&u,&v);
106             add(s,heads,sn,u,v,0);
107             add(s,heads,sn,v,u,0);
108         }
109         for(int i=1;i<=n;i++)
110             scanf("%d",&ww[i]);
111         dfs1(1);
112         for(int i=1;i<=m;i++)
113         {
114             scanf("%d%d",&u,&v);
115             p[i].u=u,p[i].v=v;
116             p[i].lca=getlca(u,v);
117             pathu[p[i].u]++;
118             add(pathv,headv,vn,p[i].v,p[i].u,i);
119             add(patha,heada,an,p[i].lca,p[i].u,i);
120             if(dep[p[i].lca]+ww[p[i].lca]==dep[p[i].u])
121                 ans[p[i].lca]--;
122         }
123         dfs(1,0);
124         for(int i=1;i<=n;i++)
125         {
126             if(i>1)
127                 putchar(' ');
128             printf("%d",ans[i]);
129         }
130         putchar('\n');
131     }
132     return 0;
133 }
倍增倍增

  终于把树上差分给过了一遍,接下来是补上过了一遍但没写记录的树链剖分,解决完这个,想学一手qdcxk玩腻了的主席树,啊啊啊,还有一堆题没补,一堆作业没做,好忙啊啊啊

posted @ 2019-05-03 02:04  新之守护者  阅读(201)  评论(0编辑  收藏  举报