zoukankan      html  css  js  c++  java
  • 【洛谷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 ",cnt[i]); } return 0; }
    • 总结:
  • 相关阅读:
    跨站攻击与文件上传漏洞
    时光变奏曲
    概率论知识总结(1)——集合、概率和计数
    电磁学知识点提要
    解析几何
    数据库与信息系统经典例题
    复变函数知识总结——复变函数作业解答与问题注释
    复变函数知识总结(4)——共形映射
    复变函数知识总结(3)——亚纯函数与对数函数
    复变函数知识总结(2)——Cauchy理论
  • 原文地址:https://www.cnblogs.com/songjian-jiansong/p/10323450.html
Copyright © 2011-2022 走看看