HDU - 4578 | 线段树多种Lz标记 or 相等标记妙用

描述

n个数,初始全为0;m次操作,4种,包括:区间加x,区间全部改变为x,区间全乘以x,询问区间内各元素的p次方的总和(1<=p<=3)。

第一种方法

因为p很小,所以维护区间的1次方和s1、2次方和s2、3次方和s3;设定三个lz标记lza、lzm、lzc,表示加、乘、改操作。
分析区间更新操作(区间加要注意更新顺序,len为区间长度)
区间加:
s 3 = s 3 + l e n ∗ x 3 + 3 ∗ x ∗ ( s 2 + x ∗ s 1 ) s3=s3+len*x^3+3*x*(s2+x*s1) s3=s3+lenx3+3x(s2+xs1)
s 2 = s 2 + l e n ∗ x 2 + 2 ∗ x ∗ s 1 s2=s2+len*x^2+2*x*s1 s2=s2+lenx2+2xs1
s 1 = s 1 + l e n ∗ x s1=s1+len*x s1=s1+lenx
区间乘:
s 1 = s 1 ∗ x s1=s1*x s1=s1x
s 2 = s 2 ∗ x 2 s2=s2*x^2 s2=s2x2
s 3 = s 3 ∗ x 3 s3=s3*x^3 s3=s3x3
区间改:
s 1 = l e n ∗ x s1=len*x s1=lenx
s 2 = l e n ∗ x 2 s2=len*x^2 s2=lenx2
s 3 = l e n ∗ x 3 s3=len*x^3 s3=lenx3
考虑区间更新时如何设置lz标记
改操作优先级别最高,一旦改后,当前区间的加、乘lz标记清空。
为方便操作,规定先乘后加,即子节点先乘以lzm,再加上lza后可以得到正确值,要维护这一特性,当前区间乘操作时,除了lzm*=x,也要laz*=x。
区间加:
l z a = l z a + x lza = lza+x lza=lza+x
区间乘:
l z m = l z m ∗ x lzm = lzm*x lzm=lzmx
l z a = l z a ∗ x lza = lza*x lza=lzax
区间改:
l z c = x lzc = x lzc=x
l z a = 0 lza = 0 lza=0
l z m = 1 lzm = 1 lzm=1
pushdown操作
先下传改操作,再下传乘操作(注意lzm下传时更新子节点的lza),最后下传加操作。

代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<queue>
#include<vector>
#define cl (k<<1)
#define cr (k<<1|1)
#define Mid ((a[k].l+a[k].r)>>1)
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
#define bug printf("???\n")

typedef long long LL;

const int Inf=0x3f3f3f3f;
const double eps=1e-7;
const int maxn=1e5+50;
const int mod=10007;

struct N{
    int l,r;
    LL s1,s2,s3;
    LL lza,lzc,lzm;
}a[maxn*4];

void build(int k,int l,int r){
    a[k].l = l, a[k].r = r;
    a[k].lzm = 1;
    a[k].lzc = 0;
    a[k].lza = 0;
    a[k].s1 = a[k].s2 = a[k].s3 = 0;
    if(l==r) return;
    build(cl, l, Mid);
    build(cr, Mid+1, r);
}
void up(int k){
    a[k].s1 = (a[cl].s1 + a[cr].s1)%mod;
    a[k].s2 = (a[cl].s2 + a[cr].s2)%mod;
    a[k].s3 = (a[cl].s3 + a[cr].s3)%mod;
}
void down(int k){
    LL lenl=(a[cl].r-a[cl].l+1)%mod;
    LL lenr=(a[cr].r-a[cr].l+1)%mod;
    if(a[k].lzc){
        a[cl].lzc = a[cr].lzc = a[k].lzc;
        a[cl].lzm = a[cr].lzm = 1;
        a[cl].lza = a[cr].lza = 0;

        LL x=a[k].lzc;
        a[cl].s1 = lenl*x %mod;
        a[cl].s2 = lenl*x*x %mod;
        a[cl].s3 = lenl*x*x*x %mod;
        a[cr].s1 = lenr*x %mod;
        a[cr].s2 = lenr*x*x %mod;
        a[cr].s3 = lenr*x*x*x %mod;
        a[k].lzc = 0;
    }
    if(a[k].lzm!=1){
        a[cl].lzm = a[cl].lzm*a[k].lzm%mod;
        a[cr].lzm = a[cr].lzm*a[k].lzm%mod;
        a[cl].lza = a[cl].lza*a[k].lzm%mod;
        a[cr].lza = a[cr].lza*a[k].lzm%mod;

        LL x=a[k].lzm;
        a[cl].s1 = a[cl].s1*x %mod;
        a[cl].s2 = a[cl].s2*x*x %mod;
        a[cl].s3 = a[cl].s3*x*x*x %mod;
        a[cr].s1 = a[cr].s1*x %mod;
        a[cr].s2 = a[cr].s2*x*x %mod;
        a[cr].s3 = a[cr].s3*x*x*x %mod;
        a[k].lzm = 1;
    }
    if(a[k].lza){
        a[cl].lza = (a[cl].lza + a[k].lza)%mod;
        a[cr].lza = (a[cr].lza + a[k].lza)%mod;

        LL x=a[k].lza;
        a[cl].s3 = (a[cl].s3 + lenl*x*x*x%mod + 3*x*(a[cl].s2 + x*a[cl].s1) )%mod;
        a[cl].s2 = (a[cl].s2 + lenl*x*x%mod + 2*x*a[cl].s1)%mod;
        a[cl].s1 = (a[cl].s1 + lenl*x)%mod;
        a[cr].s3 = (a[cr].s3 + lenr*x*x*x%mod + 3*x*(a[cr].s2 + x*a[cr].s1) )%mod;
        a[cr].s2 = (a[cr].s2 + lenr*x*x%mod + 2*x*a[cr].s1)%mod;
        a[cr].s1 = (a[cr].s1 + lenr*x)%mod;
        a[k].lza = 0;
    }
}
void update(int k,int l,int r,LL x, int tp){ //1-add 2-mul 3-change
    if(l<=a[k].l&&a[k].r<=r){
        LL len=(a[k].r-a[k].l+1)%mod;
        if(tp==3){  //change
            a[k].lzm = 1;
            a[k].lza = 0;
            a[k].lzc = x;
            a[k].s1 = len*x%mod;
            a[k].s2 = len*x*x%mod;
            a[k].s3 = len*x*x*x%mod;
        }
        else
        if(tp==2){  //mul
            a[k].lza = a[k].lza*x%mod;
            a[k].lzm = a[k].lzm*x%mod;
            a[k].s1 = a[k].s1*x%mod;
            a[k].s2 = a[k].s2*x*x%mod;
            a[k].s3 = a[k].s3*x*x*x%mod;
        }
        else
        if(tp==1){  //add
            a[k].lza = (a[k].lza+x)%mod;
            a[k].s3 = (a[k].s3 + len*x*x*x%mod + 3*x*(a[k].s2 + x*a[k].s1) ) %mod;
            a[k].s2 = (a[k].s2 + len*x*x%mod + 2*x*a[k].s1) %mod;
            a[k].s1 = (a[k].s1 + len*x) %mod;
        }
        return;
    }
    down(k);
    if(l<=Mid) update(cl, l, r, x, tp);
    if(r> Mid) update(cr, l, r, x, tp);
    up(k);
}
LL query(int k,int l,int r,LL p){
    if(l<=a[k].l&&a[k].r<=r){
        if(p==3)    return a[k].s3%mod;
        if(p==2)    return a[k].s2%mod;
        if(p==1)    return a[k].s1%mod;
    }
    down(k);
    LL ret=0;
    if(l<=Mid) ret = (ret + query(cl, l, r, p))%mod;
    if(r> Mid) ret = (ret + query(cr, l, r, p))%mod;
    return ret;
}
int main()
{
    int n,m;
    while(cin>>n>>m, n+m){
        build(1, 1, n);
        int tp,l,r;
        LL x;
        for(int i=1; i<=m; i++){
            scanf("%d%d%d%lld",&tp,&l,&r,&x);
            if(tp<=3)   update(1, l, r, x, tp);
            else        printf("%lld\n",query(1,l,r,x));
        }   
    }
}
第二种方法

此题有一个特殊的前提,初始数据全部为0,且后续操作中有将区间全部改为同一数据,所以可以设置相等标记eq,表示当前区间内值是否一致,每次更新和询问时,只有下传到数据全部相等的区间才操作,区间值相同大大减轻了操作复杂度。面对题目不知所措不妨一试,万一数据水呢?

代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<queue>
#include<vector>
#define cl (k<<1)
#define cr (k<<1|1)
#define Mid ((a[k].l+a[k].r)>>1)
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
#define bug printf("???\n")

typedef long long LL;

const int Inf=0x3f3f3f3f;
const double eps=1e-7;
const int maxn=1e5+50;
const int mod=10007;

struct N{
    int l,r,v,eq;
}a[maxn*4];

void build(int k,int l,int r){
    a[k].l=l; a[k].r=r;
    a[k].v=0; a[k].eq=1;
    if(l==r) return;
    build(cl,l,Mid);
    build(cr,Mid+1,r);
}
void down(int k){
    a[cl].eq = a[cr].eq = 1;
    a[cl].v = a[cr].v = a[k].v;
    a[k].eq = 0;
}
void update(int k,int l,int r,int x,int tp){
    if(l<=a[k].l&&a[k].r<=r && a[k].eq){
        if(tp==1){
            a[k].v = (a[k].v+x)%mod;
        }
        if(tp==2){
            a[k].v = (a[k].v*x)%mod;
        }
        if(tp==3){
            a[k].v = x%mod;
        }
        a[k].eq = 1;
        return;
    }
    if(a[k].eq) down(k);
    if(l<=Mid) update(cl,l,r,x,tp);
    if(r> Mid) update(cr,l,r,x,tp);

    if(a[cl].v==a[cr].v && a[cl].eq && a[cr].eq)
         a[k].eq=1, a[k].v=a[cl].v;
    else    
        a[k].eq = 0;
}
int query(int k,int l,int r,int p){
    if(l<=a[k].l&&a[k].r<=r && a[k].eq){
        int len=(a[k].r-a[k].l+1);
        if(p==1){
            return a[k].v*len%mod;
        }
        if(p==2){
            return a[k].v*a[k].v%mod*len%mod;
        }
        if(p==3){
            return a[k].v*a[k].v%mod*a[k].v%mod*len%mod;
        }
    }
    if(a[k].eq) down(k);
    int ret=0;
    if(l<=Mid) ret = (ret + query(cl,l,r,p))%mod;
    if(r> Mid) ret = (ret + query(cr,l,r,p))%mod;
    return ret;
}

int main()
{
    int n,m;
    while(cin>>n>>m, n+m){
        build(1, 1, n);
        int tp,l,r;
        int x;
        for(int i=1; i<=m; i++){
            scanf("%d%d%d%d",&tp,&l,&r,&x);
            if(tp<=3)   update(1, l, r, x, tp);
            else        printf("%d\n",query(1,l,r,x));
        }   
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值