牛客算法周周练15 d 树上求和(dfs序将树结构转变成线性结构 再用线段树进行维护)

题目链接 https://ac.nowcoder.com/acm/contest/6290#question

这题被自己挖了好多坑 一共wa了十一法
好在最后六分钟a掉了
一开始有输出 但是一直wa我还以为是取模的问题
结果wa了六七发 觉得不太对劲
自己造了个数据 发现我建树的时候数据赋值有问题
改了快一个小时 才改过来
后来取模还是过不了 才发现我的mod一直开的是2333 但题目要求的是23333
被自己坑哭

题目思路

题目给出一个树 要对数进行两个操作
1 对结点x的子树所有节点加y(包括x点)
2 查询结点x的子树值的平方和(包括x点)

这题看要求就知道是用线段树维护区间和和区间平方和
区间和很简单 线段树入门操作
区间平方和 也很简单
令区间和为sum 要加的值为y 区间长度为len
他的改变值 其实就是(2*sum * y+len * y * y)(类比完全平方公式)
然后操作二也是线段树基本操作

还要注意的就是题目给的是一棵树上的值
所以要先求出dfs序
并且在建树时赋值要注意 具体事项代码中再写

ac代码

(不是很知道咋去模 所以不要吐槽我沙雕的取模操作 球球了)

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
using namespace std;
const int maxn = 1e5+10;
const int inf = 0x1f1f1f1f;
const ll llinf =1e17+10;
const int mod = 2333;

ll a[maxn];
struct node
{
    int to,next;
}e[maxn*2];

int first[maxn],len,tim,in[maxn],out[maxn],pos[maxn];
ll t[maxn<<2],ans[maxn<<2],lazy[maxn<<2],lens[maxn<<2];


void add(int u,int v)
{
    e[len].to=v;
    e[len].next=first[u];
    first[u]=len++;
}

void dfs(int x,int fa)
{
    in[x]=++tim;
    pos[in[x]]=x;//这里要标记一下每个位置对应的a数组的位置 方便建树时赋值
    for(int i=first[x];i!=-1;i=e[i].next)
    {
        int to=e[i].to;
        if(to==fa)continue;
        dfs(to,x);
    }
    out[x]=tim;
}

void pushup(int rt)
{
    t[rt]=t[lson]+t[rson];
    ans[rt]=ans[lson]+ans[rson];
}

void pushdown(int rt)
{
    if(lazy[rt])
    {
        lazy[lson]+=lazy[rt];
        lazy[rson]+=lazy[rt];
        ans[lson]=(ans[lson]%mod+2*lazy[rt]%mod*t[lson]%mod+lens[lson]%mod*lazy[rt]%mod*lazy[rt]%mod)%mod;
        ans[rson]=(ans[rson]%mod+2*lazy[rt]%mod*t[rson]%mod+lens[rson]%mod*lazy[rt]%mod*lazy[rt]%mod)%mod;
        t[lson]+=lazy[rt]*lens[lson];
        t[rson]+=lazy[rt]*lens[rson];
        lazy[rt]=0;
    }
}

void build(int l,int r,int rt)
{
    lens[rt]=r-l+1;
    if(l==r)
    {
        t[rt]=a[pos[l]];//这里之前写a[l] wa了我七发 后来找bug也找到想死 真的要注意这些细节
        ans[rt]=a[pos[l]]*a[pos[l]]%mod;
        //printf("rt %d %lld\n",pos[l],a[pos[l]]);
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}

void change(int l,int r,int L,int R,int rt,ll v)
{
    if(L<=l&&r<=R)
    {
        ans[rt]+=2*v*t[rt]+v*v*lens[rt];
        t[rt]+=v*lens[rt];
        lazy[rt]+=v;
        return;
    }
    int mid=(l+r)>>1;
    pushdown(rt);
    if(L<=mid)
        change(l,mid,L,R,lson,v);
    if(R>mid)
        change(mid+1,r,L,R,rson,v);
    pushup(rt);
}

ll query(int l,int r,int L,int R,int rt)
{
    if(L<=l&&r<=R)
    {
        return ans[rt];
    }
    int mid=(l+r)>>1;
    pushdown(rt);
    ll res=0;
    if(L<=mid)
        res+=query(l,mid,L,R,lson)%mod;
    if(R>mid)
        res+=query(mid+1,r,L,R,rson)%mod;
    return res;
}

int main()
{
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    int n,q;
    ms(first,-1);
    ll sum=0;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1,-1);
    build(1,n,1);
    while(q--)
    {
        int type,x;
        ll y;
        scanf("%d",&type);
        if(type==1)
        {
            scanf("%d%lld",&x,&y);
            int l=in[x];
            int r=out[x];
            change(1,n,l,r,1,y);
        }
        if(type==2)
        {
            scanf("%d",&x);
            int l=in[x];
            int r=out[x];
            ll sum=query(1,n,l,r,1)%mod;
            printf("%lld\n",sum);
        }
    }
}

写了这么多天线段树 终于在比赛里面自己写出一道线段树的题了
虽然过程非常艰难(主要还是自己演) 但是结果还是好的
证明我这些天啃专题还是有用的 继续加油吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值