P3373 【模板】线段树 2

44 篇文章 1 订阅
8 篇文章 0 订阅

P3373

AC代码:

这里的延迟标记要开两个,分别记录加法的值和乘法的值,但是乘法和加法的优先级不一样,不规定他们的顺序的话会有错误,所以可以规定乘法优先,即规定好该结点的值等于该节点的值*父节点的乘法延迟标记的值+父节点加法延迟标记的值*区间长度,即,sum[num * 2] = (sum[num * 2] * Add[num].wc + ln * Add[num].wj) % mod;这样的话假如改变wj的数值就只改变wj,改变wc的时候把wc也对应的乘一下就可以了。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 100100;
ll n, m, mod;
ll a[maxn];
ll sum[maxn * 4];

struct A
{
    ll flag;
    ll wc, wj;
} Add[maxn * 4];

void PushUp(ll n)
{
    sum[n] = (sum[n * 2] + sum[n * 2 + 1]) % mod;
    return;
}
//建树
void Build(ll l, ll r, ll num)
{
    if (l == r)
    {
        sum[num] = a[l];
        return;
    }
    ll m = (l + r) / 2;
    Build(l, m, num * 2);
    Build(m + 1, r, num * 2 + 1);
    PushUp(num);
    return;
}

void PushDown(ll num, ll ln, ll rn) 
{
    if (Add[num].flag)
    {
        sum[num * 2] = (sum[num * 2] * Add[num].wc + ln * Add[num].wj) % mod;
        sum[num * 2 + 1] = (sum[num * 2 + 1] * Add[num].wc + Add[num].wj * rn) % mod;

        Add[num * 2].wj = (Add[num * 2].wj * Add[num].wc + Add[num].wj) % mod;
        Add[num * 2 + 1].wj = (Add[num * 2 + 1].wj * Add[num].wc + Add[num].wj) % mod;

        Add[num * 2].wc = (Add[num * 2].wc * Add[num].wc) % mod;
        Add[num * 2 + 1].wc = (Add[num * 2 + 1].wc * Add[num].wc) % mod;

        Add[num].flag = 0;
        Add[num].wj = 0;
        Add[num].wc = 1;
        Add[num * 2].flag = 1;
        Add[num * 2 + 1].flag = 1;
    }
    return;
}

void Update(ll op, ll L, ll R, ll C, ll l, ll r, ll num)
{
    if (R < l || L > r)
        return ;
    if (l >= L && R >= r)
    {
        if(op==1)
        {
            sum[num] = (sum[num] + C * (r - l + 1)) % mod;
            Add[num].wj = ( Add[num].wj + C ) % mod;
            Add[num].flag = 1;
            return;
        }
        if(op==-1)
        {
            sum[num] = (sum[num] * C) % mod;
            Add[num].wc = (Add[num].wc * C) % mod;
            Add[num].wj = (Add[num].wj * C) % mod;
            Add[num].flag = 1;
            return;
        }
        return;
    }
    ll m = (l + r) / 2;
    PushDown(num, m - l + 1, r - m); //下推标记
    if (L <= m)
        Update(op, L, R, C, l, m, num * 2);
    if (R >= m + 1)
        Update(op, L, R, C, m + 1, r, num * 2 + 1);
    PushUp(num);
    return;
}

//区间查询  查询区间 L-R
ll Query(ll L, ll R, ll l, ll r, ll num)
{
    if (R < l || L > r)
        return 0;
    if (L <= l && R >= r)
    {
        return sum[num]%mod;
    }
    ll m = (l + r) / 2;
    PushDown(num, m - l + 1, r - m);
    ll ans = 0;
    if (L <= m)
        ans += Query(L, R, l, m, num * 2);
    if (R >= m + 1)
        ans += Query(L, R, m + 1, r, num * 2 + 1);
    return ans%mod;
}


int main()
{
    for (int i = 0; i < maxn * 4;i++)
    {
        Add[i].flag = 0;
        Add[i].wc = 1;
        Add[i].wj = 0;
    }
    ll x, y, k, z;
    scanf("%lld %lld %lld", &n, &m, &mod);
    for (int i = 1; i <= n;i++)
        scanf("%lld", &a[i]);
    Build(1, n, 1);
    while (m--)
    {
        scanf("%lld", &z);
        if(z==1)
        {
            scanf("%lld %lld %lld", &x, &y, &k);
            Update(-1, x, y, k%mod, 1, n, 1);
        }
        else if(z==2)
        {
            scanf("%lld %lld %lld", &x, &y, &k);
            Update(1, x, y, k%mod, 1, n, 1);
        }
        else if(z==3)
        {
            scanf("%lld %lld", &x, &y);
            printf("%lld\n", Query(x, y, 1, n, 1));
        }
    }

    return 0;
}

刚开始的思路也是开两个延迟标记分别记录,然后因为没想到怎么处理乘法和加法的优先级问题,所以干脆直接推下去算了(如果给该节点标记乘法延迟标记的时候发现该结点已经存在加法的延迟标记,就把加法的延迟标记推下去(这里是一个递归的过程),再标记乘法的延迟标记),这样就保证了一个节点只会存在一种延迟标记,但是他T了。。

T的代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 100100;
ll n, m, mod;
ll a[maxn];
ll sum[maxn * 4];

struct A
{
    ll flag;
    ll wc,wj;
} Add[maxn * 4];

void PushUp(ll n)
{
    sum[n] = (sum[n * 2] + sum[n * 2 + 1]) % mod;
    return;
}
//建树
void Build(ll l, ll r, ll num)
{
    if (l == r)
    {
        sum[num] = a[l];
        return;
    }
    ll m = (l + r) / 2;
    Build(l, m, num * 2);
    Build(m + 1, r, num * 2 + 1);
    PushUp(num);
    return;
}

void PushDown(ll num, ll ln, ll rn, ll l, ll r) 
{
    if (ln < 0 || rn < 0)
        return;
    if (l >= r)
        return;
    if (num >= maxn)
        return;
    if (Add[num].flag == 1)
    {
        if (Add[num * 2].flag != 0)
        {
            ll lll = l;
            ll rrr = (l + r) / 2;
            ll mid = (lll + rrr) / 2;
            PushDown(num * 2, mid - lll + 1, rrr - mid, lll, rrr);
        }
        if (Add[num * 2 + 1].flag != 0)
        {
            ll lll = (l + r) / 2 + 1;
            ll rrr = r;
            ll mid = (lll + rrr) / 2;
            PushDown(num * 2 + 1, mid - lll + 1, rrr - mid, lll, rrr);
        }
        Add[num * 2].wj = Add[num * 2].wj % mod + Add[num].wj % mod;
        Add[num * 2 + 1].wj = Add[num * 2 + 1].wj % mod + Add[num].wj % mod;

        Add[num * 2].flag = 1;
        Add[num * 2 + 1].flag = 1;

        sum[num * 2] = sum[num * 2] % mod + ((Add[num].wj % mod) * (ln % mod)) % mod;
        sum[num * 2 + 1] = sum[num * 2 + 1] % mod + ((Add[num].wj % mod) * (rn % mod)) % mod;
        Add[num].flag = 0;
        Add[num].wj = 0;
        }
    if (Add[num].flag==-1) 
    {
        if (Add[num * 2].flag != 0)
        {
            ll lll = l;
            ll rrr = (l + r) / 2;
            ll mid = (lll + rrr) / 2;
            PushDown(num * 2, mid - lll + 1, rrr - mid, lll, rrr);
        }
        if (Add[num * 2 + 1].flag != 0)
        {
            ll lll = (l + r) / 2 + 1;
            ll rrr = r;
            ll mid = (lll + rrr) / 2;
            PushDown(num * 2 + 1, mid - lll + 1, rrr - mid, lll, rrr);
        }
        Add[num * 2].wc = ((Add[num * 2].wc % mod) * (Add[num].wc % mod)) % mod;
        Add[num * 2 + 1].wc = ((Add[num * 2 + 1].wc % mod) * (Add[num].wc % mod)) % mod;

        Add[num * 2].flag = -1;
        Add[num * 2 + 1].flag = -1;

        sum[num * 2] = ((sum[num * 2] % mod) * (Add[num].wc % mod)) % mod;
        sum[num * 2 + 1] = ((sum[num * 2 + 1] % mod) * (Add[num].wc % mod)) % mod;
        Add[num].flag = 0;
        Add[num].wc = 1;
    }
    return;
}

void Update(ll op, ll L, ll R, ll C, ll l, ll r, ll num)
{
    if (R < l || L > r)
        return ;
    if (l >= L && R >= r)
    {
        if(op==1)
        {
            if(Add[num].flag==-1)
            {
                ll m = (l + r) / 2;
                PushDown(num, m - l + 1, r - m, l, r);
            }
            sum[num] =((sum[num]%mod) + ( C * (r - l + 1) )%mod)%mod;
            Add[num].wj = ((Add[num].wj % mod) + (C % mod)) % mod;
            Add[num].flag = op;
            return;
        }
        if(op==-1)
        {
            if(Add[num].flag==1)
            {
                ll m = (l + r) / 2;
                PushDown(num, m - l + 1, r - m, l, r);
            }
            sum[num] = ((sum[num] % mod) * (C % mod)) % mod;
            Add[num].wc = ((Add[num].wc % mod) * (C % mod)) % mod;
            Add[num].flag = op;
            return;
        }
        return;
    }
    ll m = (l + r) / 2;
    PushDown(num, m - l + 1, r - m, l, r); //下推标记
    if (L <= m)
        Update(op, L, R, C, l, m, num * 2);
    if (R >= m + 1)
        Update(op, L, R, C, m + 1, r, num * 2 + 1);
    PushUp(num);
    return;
}

//区间查询  查询区间 L-R
ll Query(ll L, ll R, ll l, ll r, ll num)
{
    if (R < l || L > r)
        return 0;
    if (L <= l && R >= r)
    {
        return sum[num]%mod;
    }
    ll m = (l + r) / 2;
    PushDown(num, m - l + 1, r - m, l, r);
    ll ans = 0;
    if (L <= m)
        ans += Query(L, R, l, m, num * 2);
    if (R >= m + 1)
        ans += Query(L, R, m + 1, r, num * 2 + 1);
    return ans%mod;
}


int main()
{
    for (int i = 0; i < maxn * 4;i++)
    {
        Add[i].flag = 0;
        Add[i].wc = 1;
        Add[i].wj = 0;
    }
    ll x, y, k, z;
    scanf("%lld %lld %lld", &n, &m, &mod);
    for (int i = 1; i <= n;i++)
        scanf("%lld", &a[i]);
    Build(1, n, 1);
    while (m--)
    {
        scanf("%lld", &z);
        if(z==1)
        {
            scanf("%lld %lld %lld", &x, &y, &k);
            Update(-1, x, y, k%mod, 1, n, 1);
        }
        else if(z==2)
        {
            scanf("%lld %lld %lld", &x, &y, &k);
            Update(1, x, y, k%mod, 1, n, 1);
        }
        else if(z==3)
        {
            scanf("%lld %lld", &x, &y);
            printf("%lld\n", Query(x, y, 1, n, 1));
        }
    }

    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值