【专题属性】树状数组

High-level ancients(UESTC_1653)

题目大意:

给一棵多叉树。初始所有结点的权值为0
有两种操作
1.add
ADD u val
以u为根的子树
u的权值+val
u的下一层儿子权值+val+1
下下一层儿子权值+val+2
类推...
2.Query
Q x
询问以x为根的子树(包含x)的权值和

结点个数N<=50000 询问个数 P<=100000 0<=val<=1000


题解:

dfs一遍整棵树  记录进结点和出结点的时间戳 得到dfs序 A

idx[u] idy[u] u进出的时间戳

tot[u] 以u为根的结点个数

tot_h[u] 以u为根的所有子结点(包括u)高度和

val[]       每在u添加一个x,val[idx[u]]+=x-h[u]

                           val[idy[u]]-=x-h[u];

time_u[u]   每在u添加一个x, time_u[idx[u]]+=1

                             time_u[idy[u]]-=1

sum_u[idx[u]] 每在u添加一个x,sum_u+=(x-h[u])*tot[u]+tot_h[u];

ANS=tot_h[u]*Sigma_time(1,idx[fa[u]])+Sigma_val(1,idx[fa[u]])*tot[u]+Sigma_sum(idx[u],idy[u]-1);

询问u的答案就是 ANS

因为只涉及修改和求和操作。维护3个树状数组即可。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define maxn 50050

using namespace std;

int id;
long long fa[maxn],q[maxn*2],tot[maxn],h[maxn],idx[maxn],idy[maxn],tot_h[maxn];
long long  val[maxn*2],time_u[maxn*2],sum_u[maxn*2];
vector <int> graph[maxn];

void dfs(int u,int deep)
{
    tot_h[u]=h[u]=deep;
    q[++id]=u;
    idx[u]=id;
    tot[u]=1;

    for (int i=0;i<graph[u].size();i++)
    {
        dfs(graph[u][i],deep+1);
        tot_h[u]+=tot_h[graph[u][i]];
        tot[u]+=tot[graph[u][i]];
    }

    q[++id]=u;
    idy[u]=id;
}

int lowbit(int x)
{
    return x&(-x);
}

void add(int x,int pos,long long a[])
{
    while (x<=id)
    {
        a[x]+=pos;
        x+=lowbit(x);
    }
}

long long sum(int x,long long a[])
{
   long long tot=0;
   while (x>0)
   {
       tot+=a[x];
       x-=lowbit(x);
   }
   return tot;
}


int main()
{
    int tt,cal=0;
    scanf("%d",&tt);
    while (tt--)
    {
        cal++;
        printf("Case #%d:\n",cal);
        int n,m;
        scanf("%d%d",&n,&m);

        for (int i=1;i<=n;i++)
        graph[i].clear();

        for (int i=2;i<=n;i++)
        {
            scanf("%lld",&fa[i]);
            graph[fa[i]].push_back(i);
        }

        id=0;
        dfs(1,1);
        memset(val,0,sizeof(val));
        memset(sum_u,0,sizeof(sum_u));
        memset(time_u,0,sizeof(time_u));
        fa[1]=0;idx[0]=idy[0]=0;

        while (m--)
        {
            char str[2];
            scanf("%s",str);
            if (str[0]=='Q')
            {
                int u;
                scanf("%d",&u);
                long long ans;

                ans=tot_h[u]*sum(idx[fa[u]],time_u)+sum(idx[fa[u]],val)*tot[u]+sum(idy[u]-1,sum_u)-sum(idx[u]-1,sum_u);
                printf("%lld\n",ans);
            } else
            {
                int u,x;
                scanf("%d%d",&u,&x);
                add(idx[u],x-h[u],val);
                add(idy[u],-x+h[u],val);
                add(idx[u],1,time_u);
                add(idy[u],-1,time_u);
                add(idx[u],(x-h[u])*tot[u]+tot_h[u],sum_u);
            }
        }

    }
    return 0;
}





在算法竞赛中,正确选择数据结构是提高算法效率的关键。《深入浅出算法竞赛》这本书将为你提供丰富的知识和实例,帮助你掌握如何根据问题特性来选择合适的数据结构。 参考资源链接:[《深入浅出算法竞赛》:详解核心知识点与实战技巧](https://wenku.youkuaiyun.com/doc/5jq48br5sb) 选择数据结构时,首先要分析问题的需求。例如,如果你需要频繁地查找最大值或最小值,那么使用堆(heap)数据结构将是一个很好的选择。堆可以高效地支持插入和删除最大(或最小)元素的操作。 如果问题涉及大量的点,并且需要快速地查询和更新点的属性,那么树状数组(Fenwick Tree)或线段树(Segment Tree)可能是更合适的选择。这两种数据结构能够支持区间查询和更新操作,在处理动态数据时非常高效。 在需要快速访问元素的情况下,哈希表(Hash Table)是一个理想的选择,它可以将键映射到值,以常数时间复杂度进行查找、插入和删除操作。 另外,图论问题中常用的邻接表和邻接矩阵,以及在排序和查找问题中常用的数组、链表、栈和队列等,都是根据问题特性选择数据结构的典型例子。 通过学习《深入浅出算法竞赛》,你将能够通过具体实例,如解决最小生成树、最短路径等问题时如何选择合适的数据结构,来深入理解这些概念。每个实例不仅展示了数据结构的应用,还讲解了算法的实现和优化方法,帮助你更好地掌握这些技巧并应用到实际的算法竞赛中。 在掌握核心知识点后,你将能够更有效地解决算法竞赛中的问题,并提升你的编程和问题解决能力。如果你希望进一步深入学习,这本书还提供了高级专题和创造性思维的培养,引导你不仅掌握算法,还能够在比赛中展示出创造性的解决方案。 参考资源链接:[《深入浅出算法竞赛》:详解核心知识点与实战技巧](https://wenku.youkuaiyun.com/doc/5jq48br5sb)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值