• ZJOI2017 day2 T2 线段树 想法题


    考完D2发现自己简直zz了。。。花式扔基本分

    首先这道题有个显然的套路:树上一些点到一个定点的距离和=这些点深度和+点数*定点深度和-2*lca深度和

    ——上一次见这个套路是LNOI2014,上次做的时候还比较naive:http://www.cnblogs.com/wanglichao/p/6425893.html

    这次考场上也只想到这一步了,,并没有发现广义线段树的奇特性质

    奇特性质:被选中的从左到右一定是一串右儿子和一串左儿子,而且都是挂在l-1到r+1上的连续右(左)儿子

    这么一来,一个询问可以分成两部分,这两部分都可以O(n)预处理出来

    在预处理的时候考虑维护两个东西:从根节点到当前点的链上所直接挂的所有右儿子的个数(记为sum[i])和深度和(sumd[i])

    那么在统计的时候 这些点深度和+点数*定点深度和-2*lca深度和 可以轻松算出

    (一脸懵逼.jpg)

    Q:如何算lca深度和?

    A:分类讨论,计算询问的定点挂到链上是哪里(以下称为悬挂点x)

    ①悬挂点以上的点与定点的lca深度为自己深度-1,所以总和为“sumd[x]-sum[x]”

    ②悬挂点的右儿子如果被算在答案里,那么深度就是"dep[x]+1"

    ③悬挂点以下的点与定点的lca深度一定为dep[x],总和为"(sum[l-1]-sum[x])*dep[x]"

    求和即可(注意细节)

    左儿子同理

    Q:l=1或者r=n怎么办

    A:只算半边(自行理解,不可言传)

    上个代码冷静一下(目前uoj上最短榜第一来自这个代码微改@wzf2000):

     1 #include <bits/stdc++.h> 
     2 #define bel(x,y) (L[x]>=L[y] && R[x]<=R[y])
     3 using namespace std;
     4 long long n,N,IN,m,u,l,r,LOG;
     5 long long mer[800001],ls[800001],rs[800001],sum[800001][2],dep[800001],sd[800001][2];
     6 long long fa[800001][20];
     7 long long L[800001],R[800001],nod[800001];
     8 long long lca(long long a,long long b)
     9 {
    10     if(bel(a,b)) return b;
    11     if(bel(b,a)) return a;
    12     long long now=a;
    13     for(long long i=LOG;i>=0;i--)
    14     if(fa[now][i] && !bel(b,fa[now][i])) now=fa[now][i];
    15     return fa[now][0];
    16 }
    17 void Dfs(long long now)
    18 {
    19     if(!ls[now]) return;
    20     sum[rs[now]][0]=sum[now][0];
    21     sum[rs[now]][1]=sum[now][1]+1;
    22     sum[ls[now]][0]=sum[now][0]+1;
    23     sum[ls[now]][1]=sum[now][1];
    24     dep[ls[now]]=dep[now]+1;
    25     dep[rs[now]]=dep[now]+1;
    26     sd[rs[now]][0]=sd[now][0];
    27     sd[rs[now]][1]=sd[now][1]+dep[now]+1;
    28     sd[ls[now]][0]=sd[now][0]+dep[now]+1;
    29     sd[ls[now]][1]=sd[now][1];
    30     Dfs(ls[now]);
    31     Dfs(rs[now]);
    32 }
    33 long long dfs(long long l,long long r)
    34 {
    35     long long now=++N;
    36     L[now]=l;R[now]=r;
    37     for(long long i=0;fa[fa[now][i]][i];i++) fa[now][i+1]=fa[fa[now][i]][i];
    38     if(l==r) return nod[l]=now;
    39     long long me=mer[IN++];
    40     fa[N+1][0]=now;
    41     ls[now]=dfs(l,me);
    42     fa[N+1][0]=now;
    43     rs[now]=dfs(me+1,r);
    44     return now;
    45 }
    46 long long getl(long long l)
    47 {
    48     long long lc=lca(l,u);
    49     long long now=dep[lc]*(sum[l][0]-sum[lc][0])+(bel(l,ls[lc]) && lc!=u)+sd[lc][0]-sum[lc][0];
    50     long long ans=sd[l][0]+sum[l][0]*dep[u]-now*2;
    51     return ans;
    52 }
    53 long long getr(long long r)
    54 {
    55     long long lc=lca(r,u);
    56     long long now=dep[lc]*(sum[r][1]-sum[lc][1])+(bel(r,rs[lc]) && lc!=u)+sd[lc][1]-sum[lc][1];
    57     long long ans=sd[r][1]+sum[r][1]*dep[u]-now*2;
    58     return ans;
    59 }
    60 int main()
    61 {
    62     scanf("%d",&n);
    63     for(long long i=1;i<=n;i<<=1)
    64         LOG++;
    65     for(long long i=1;i<n;i++)
    66         scanf("%d",&mer[i]);
    67     IN=1;
    68     dfs(1,n);
    69     Dfs(1);
    70     scanf("%d",&m);
    71     for(long long i=1;i<=m;i++)
    72     {
    73         scanf("%d%d%d",&u,&l,&r);
    74         --l;++r;
    75         if(!l && r>n)
    76         {
    77             printf("%d
    ",dep[u]);
    78             continue;
    79         }
    80         long long ans=0;
    81         if(l)
    82             ans+=getl(nod[l])-((r<=n)?getl(ls[lca(nod[l],nod[r])]):0);
    83         if(r<=n)
    84             ans+=getr(nod[r])-(l?getr(rs[lca(nod[l],nod[r])]):0);
    85         printf("%lld
    ",ans);
    86     }
    87     return 0;
    88 }
  • 相关阅读:
    flowable ui 界面请假流程操作实例
    访谷歌看油管 这里有你想要的!!!
    Termux中使用数据线传输文件
    Vue中使用ECharts图表展示数据
    vue项目使用阿里图标库图标
    linux下使用命令行查看天气预报情况
    在博客或者其他Html页面新增浮动音乐播放器
    Vue实现弹窗拖动放大缩小
    分享一个IDEA 免费注册码分享站点
    远程命令执行之------电脑自动执行页面输入的指令
  • 原文地址:https://www.cnblogs.com/wanglichao/p/6816462.html
Copyright © 2020-2023  润新知