为什么树的长度开到10^5会爆,而10^6就AC了?

P3372 【模板】线段树 1

RainbowSheep_ @ 2024-10-21 19:15:51

好像我这份代码才会出现这种问题

还有,为什么要在每次区间操作的时候都要 pushdown.... 附代码:

//https://www.luogu.com.cn/problem/P3372
#include <iostream>
using namespace std;

#define MAX 100010
#define ROOT 1

struct segment
{
    int l,r;
    long long val,lazy;
}segtree[MAX*4];

int a[MAX],n,m,x,y,op;
long long k;

void build(int p,int l,int r)
{
    segtree[p].l=l;
    segtree[p].r=r;
    if(l==r)
    {
        segtree[p].val=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    segtree[p].val=segtree[p*2].val+segtree[p*2+1].val;
}

inline void pd(int p)
{
    if(!segtree[p].lazy)
        return;

    if(segtree[p].l!=segtree[p].r)
    {
        segtree[p*2].lazy+=segtree[p].lazy;
        segtree[p*2+1].lazy+=segtree[p].lazy;
    }
    segtree[p*2].val+=segtree[p].lazy*(segtree[p*2].r-segtree[p*2].l+1);
    segtree[p*2+1].val+=segtree[p].lazy*(segtree[p*2+1].r-segtree[p*2+1].l+1);
    segtree[p].lazy=0;
}

void add(long long x,int l,int r,int p=ROOT)
{
    pd(p);
//  cout << segtree[p].l << ',' << segtree[p].r << ':' << segtree[p].val << endl;
    if(l<=segtree[p].l&&r>=segtree[p].r)
    {
        segtree[p].val+=x*(segtree[p].r-segtree[p].l+1);
        segtree[p].lazy+=x;
//      cout << segtree[p].l << ',' << segtree[p].r << ':' << segtree[p].val << endl;
        return;
    }
    int mid=(segtree[p].l+segtree[p].r)/2;
    if(r<=mid)
        add(x,l,r,p*2);
    else if(l>mid)
        add(x,l,r,p*2+1);
    else
    {
        add(x,l,r,p*2);
        add(x,l,r,p*2+1);
    }
    segtree[p].val=segtree[p*2].val+segtree[p*2+1].val;
//  cout << segtree[p].l << ',' << segtree[p].r << ':' << segtree[p].val << endl;
}

long long query(int l,int r,int p=ROOT)
{
    pd(p);
    if(l<=segtree[p].l&&r>=segtree[p].r)
        return segtree[p].val;

    int mid=(segtree[p].l+segtree[p].r)/2;
    if(r<=mid)
        return query(l,r,p*2);
    else if(l>mid)
        return query(l,r,p*2+1);

    return query(l,r,p*2)+query(l,r,p*2+1);
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 1;i<=n;i++)
        cin >> a[i];

    build(ROOT,1,n);
    while(m--)
    {
        cin >> op >> x >> y;
        switch(op)
        {
        case 1:
            cin >> k;
            add(k,x,y);
            break;
        case 2:
            cout << query(x,y) << endl;
        }
    }
    return 0;
}

by Hagasei @ 2024-10-21 19:18:08

这个写法线段树数组得开到 8n。


by RainbowSheep_ @ 2024-10-21 19:18:34

@Hagasei 为什么呢 orz


by SegmentTree_ @ 2024-10-21 19:20:54

@RainbowSheep_ 你pushdown判叶子节点那里要把下面两行也放进去啊,你这样到了叶子节点也会继续传,空间就翻倍了,还有正常的线段树要开4n。


by MutU @ 2024-10-21 19:21:43

因为线段树要开四倍空间

不然呢


by Hagasei @ 2024-10-21 19:21:47

首先线段树结点数量是 2n,编号最大值是 4n。但是你的写法在叶子结点也会 pushdown 导致他的儿子编号达到了 8n。

其实当条件 l<=segtree[p].l&&r>=segtree[p].r 成立的时候就不用 pushdown 了,因为你的 pushdown 是更新儿子结点信息,而当前结点的信息是正确的。

将 pushdown 放在退出判断后面还有个好处是 pushdown 内部不用判断是不是叶子了,因为不可能。


by RainbowSheep_ @ 2024-10-21 19:22:47

@tianyu_awa 明白了,谢谢大佬 orz


by RainbowSheep_ @ 2024-10-21 19:24:39

@Hagasei 明白了,谢谢大佬


by ZMQ_Ink6556 @ 2024-10-21 21:29:06

@Hagasei 线段树的极限节点数量是 2n-1


by Hagasei @ 2024-10-22 07:45:02

@ZMQ_Ink6556 bruh 很明显我在说数量级啊。结点编号最大值也不是 4n 啊。


by ZMQ_Ink6556 @ 2024-10-22 07:48:58

@Hagasei 省略常数项节点数为 n


|