【洛谷P3258】松树的新家(点差分)

  • 题意:
  • 思路:
  • 实现:
  1. 初始化:cnt[],head[],a[],f[][30!!],depth[],node e[].;读入;双向加边;depth[1]=1(不从1开始建树也行);
  2. 建树;向上跳;遍历+走lca;深搜求子树和;cnt[2~n]要-1,不要忘!
  3. 输出
  • 代码:
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=300000+2;
    int n,x,y,k=1;
    int a[MAXN],head[MAXN],f[MAXN][30],depth[MAXN],cnt[MAXN];//树上差分加一个cnt数组记经过次数 
    struct node{
        int u,v,next;
    }e[MAXN*2];
    void add(int x,int y){
        e[k].u=x;
        e[k].v=y;
        e[k].next=head[x];
        head[x]=k++;
    }//链式前向星模拟邻接表,逆序,无影响 
    void build(int u){
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(depth[v]==0){//v没在树上 
                depth[v]=depth[u]+1;
                f[v][0]=u;
                build(v);//从v开始建树 
            }
        }
    }
    void fill(){
        for(int i=1;i<=29;i++)
            for(int j=1;j<=n;j++)
                f[j][i]=f[f[j][i-1]][i-1];//第j个节点,向上跳i能到达的节点先跳到2^(i-1)处再向上跳2^(i-1)能到达的节点 
    }
    int lca(int x,int y){
        if(depth[x]>depth[y])swap(x,y);
        for(int i=29;i>=0;i--)
            if(depth[x]<=depth[y]-(1<<i))
              y=f[y][i];
        if(x==y)
          return x;
        for(int i=29;i>=0;i--)
          if(f[x][i]==f[y][i])continue;
          else x=f[x][i],y=f[y][i];
        return f[x][0];
    }//倍增求lca 
    void dfs(int u){
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(v!=f[u][0]){//如果v不是u的祖先,则u是v的祖先,再求v的子树和 
                dfs(v);
                cnt[u]+=cnt[v];
            }
        }
    }
    int main(){
        memset(head,-1,sizeof(head));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
          scanf("%d",&a[i]);
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }//双向 
        depth[1]=1;//初始化(从1开始遍历) 
        build(1);//从1开始建树 
        fill();//向上跳

    ****** for(int i=1;i<n;i++){ x=a[i]; y=a[i+1]; cnt[x]++; cnt[y]++; cnt[lca(x,y)]--; cnt[f[lca(x,y)][0]]--; } ******

    dfs(1);//求子树和,把子树加起来

    *** for(int i=2;i<=n;i++){ cnt[a[i]]--; }//不要忘了不算an走的那次 ***
    for(int i=1;i<=n;i++){ printf("%d\n",cnt[i]); } return 0; }
  • 总结:
posted @ 2019-01-26 14:52  jiansong!  阅读(151)  评论(0编辑  收藏  举报