2 solutions

  • 4
    @ 2025-1-20 11:00:28

    用于代码对比

    #include<bits/stdc++.h>
    #define N 500001
    using namespace std;
    vector<int>edge[N];
    int dfn[N],tot,n,q,maxn,st[20][N],lg[N];
    int arr[N],fat[N],res[N];
    void dfs1(int num,int p){
    	dfn[num]=++tot;
    	fat[num]=p;
    	st[0][dfn[num]]=p;
    	for(auto i:edge[num]){
    		if(i==p)continue;
    		dfs1(i,num);
    	}
    }
    int get(int u,int v){return dfn[u]<dfn[v]?u:v;}
    void init(){
    	lg[1]=0;
    	for(int i=2;i<N;i++){
    		lg[i]=lg[i>>1]+1;
    	}
    	for(int k=1;k<20;k++){
    		for(int i=1;i+(1<<k)-1<=n;i++){
    			st[k][i]=get(st[k-1][i],st[k-1][i+(1<<(k-1))]);
    		}
    	}
    }
    int lca(int u,int v){
    	if(u==v)return u;
    	u=dfn[u],v=dfn[v];
    	if(u>v)swap(u,v);
    	int k=lg[v-u];
    	return get(st[k][u+1],st[k][v-(1<<k)+1]);
    }
    void dfs2(int num,int p){
    	for(auto i:edge[num]){
    		if(i==p)continue;
    		dfs2(i,num);
    		arr[num]+=arr[i];
    	}
    	res[num]=arr[num];
    }
    
    
    int qst[N];
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;i++){
            cin>>qst[i];
    	}
    	for(int i=1;i<n;i++){
    		int u,v;
    		cin>>u>>v;
    		edge[u].emplace_back(v);
    		edge[v].emplace_back(u);
    	}
    	dfs1(1,0);
    	init();
        for(int i=1;i<n;i++){
            arr[fat[qst[i]]]++,arr[qst[i+1]]++;
            arr[lca(qst[i],qst[i+1])]--,arr[fat[lca(qst[i],qst[i+1])]]--;
        }
    	dfs2(1,0);
        arr[qst[1]]++,arr[qst[n]]--;
        for(int i=1;i<=n;i++){
            cout<<arr[i]<<endl;
        }
    }
    
    • 2
      @ 2025-1-18 21:23:05

      原题解:https://www.luogu.com.cn/article/pcq4bpud ,转载是为了方便看

      主要讲一讲

      树上差分

      顾名思义,树上差分就是在树上的差分(???)

      学过树状数组(区间修改)的同学应该了解,差分具有很优秀的性质

      已知原数组 a[i],设差分数组 b[i]=a[i]-a[i-1]

      那么就有 a[i]=b[1]+b[2]+...+b[n]

      修改也很方便,例如对于区间(p,q)同时加上x,相当于 b[p]+=n,b[q+1]-=n


      接下来就是把线性的差分移到树上,

      同理,已知原数组a[i]表示每个点的点权,

      我们设差分数组b[i]=a[i]-sum(a[j])(j为i的每个直接相连的儿子);

      不难发现,叶子节点的b[i]恰好为该点点权a[i],此外,对于任意一个节点K,

      a[K]=以K为根节点的子树中所有b[i]之和。

      如果我们要对树上的一条链(u,v)的点权进行修改(同时加上x),只需要:

      b[u]+=x;

      b[v]+=x;

      b[lca(u,v)]-=x;

      b[fa[lca(u,v)]]-=x;

      注意为了不影响到链外的其他点权,lca(u,v)的父节点需要修改,可以自己推一下。

      这是点权的情况,如果是边权,只需要把一个点到父亲的边权赋给自己当作点权,

      然后当作点权的情况做就好了,细节需要处理一下。


      在本题中,原数组初始都是0,所以我们只需要读入、处理,最后遍历整颗树,递

      归求出a[i]就ok了


      代码:

      #include <bits/stdc++.h>
      using namespace std;
      int h[300000+5],fa[300000+5][25];
      int vis[300000+5],lg[300000+5],dep[300000+5];
      int a[300000+5],b[300000+5];//a为糖果数,b为差分数组
      int m,n,i,j,x,y,tmp;
      struct EDGE{
          int from,to,next;
      };
      EDGE e[600000+5];
      void add(int a,int b){
          tmp++;
          e[tmp].from=a;
          e[tmp].to=b;
          e[tmp].next=h[a];
          h[a]=tmp;
      }
      void dfs1(int k){
          int s=h[k];
          while(s!=0){
              int t=e[s].to;
              if(t!=fa[k][0]){
                  fa[t][0]=k;
                  dep[t]=dep[k]+1;
                  dfs1(t);
              } 
              s=e[s].next;
          }
      }
      int lca(int a,int b){
          if(dep[a]<dep[b]){
              int t=a;
              a=b;
              b=t;
          }
          while(dep[a]!=dep[b]){
              int k=lg[dep[a]-dep[b]]-1;
              a=fa[a][k];
          }
          if(a==b) return a;
          else{
              for(j=lg[dep[a]]-1;j>=0;j--){
                  if(fa[a][j]!=fa[b][j]){
                      a=fa[a][j];
                      b=fa[b][j];
                  }
              }
          }
          return fa[a][0];
      }
      void dfs2(int k){
          int s=h[k];
          while(s!=0){
              int t=e[s].to;
              if(t!=fa[k][0]){
                  dfs2(t);
                  b[k]+=b[t];
              }
              s=e[s].next;
          }
      }
      int main(){
          cin>>n;
          for(i=1;i<=n;i++)
              scanf("%d",&vis[i]); 
          for(i=1;i<=n-1;i++){
              scanf("%d%d",&x,&y);
              add(x,y);
              add(y,x);
          }
          dfs1(1);
          for(j=1;j<=20;j++)
              for(i=1;i<=n;i++)
                  fa[i][j]=fa[fa[i][j-1]][j-1];
          for(i=1;i<=n;i++)
              lg[i]=lg[i-1]+(1<<lg[i-1]==i);
          a[vis[1]]++;
          for(i=2;i<=n;i++){
          	int now=lca(vis[i],vis[i-1]);
          	b[vis[i]]++;
          	b[vis[i-1]]++;
          	b[now]--;
          	b[fa[now][0]]--;
          	a[vis[i-1]]--;//链首糖果数直接减1
          }
          a[vis[n]]--;
          dfs2(1);
          for(i=1;i<=n;i++)
              cout<<a[i]+b[i]<<endl;
          return 0;
      }

      明天生日写篇题解开心一下

    • 1

    Information

    ID
    323
    Time
    1000ms
    Memory
    125MiB
    Difficulty
    7
    Tags
    # Submissions
    19
    Accepted
    7
    Uploaded By