标记的优先运算——kuagnbin线段树专题(4)

P3373 【模板】线段树 2
HDU 4578 Transformation

洛谷那道题就是裸的区间乘法,HDU的那道题更复杂,还要求区间幂次的和

先说区间乘法的问题,很自然会想到一个时间戳的问题,因为乘法和加法之间不具有交换律,所以先乘后加先加后乘结果是不一样的。

那么真的要维护一个时间戳吗,这样显然很不好写。

考虑下方标记时的情况,我们只知道有add标记和mul标记,这时候儿子节点时先乘呢还是先加?
我们在处理标记的时候这样处理,每次有一个mul标记加进来,就把add标记也更新为add*mul,那么之后下放标记就可以先乘后加了,因为所有在乘法之前的add标记都已经被乘过一次了。

P3373 【模板】线段树 2
区间乘法裸题

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int INF = 1<<30;
const int maxn = 1e5+5;
struct node
{
    int l,r;
    ll sum,mul,add;
}tree[maxn<<2];
int n,m,p;
void push_down(int id)
{
    int lson=id<<1,rson=id<<1|1;
    tree[lson].sum = (tree[lson].sum*tree[id].mul%p+tree[id].add*(tree[lson].r-tree[lson].l+1)%p)%p;
    tree[lson].add = (tree[lson].add*tree[id].mul%p+tree[id].add)%p;
    tree[lson].mul = (tree[lson].mul*tree[id].mul)%p;
    tree[rson].sum = (tree[rson].sum*tree[id].mul%p+tree[id].add*(tree[rson].r-tree[rson].l+1)%p)%p;
    tree[rson].add = (tree[rson].add*tree[id].mul%p+tree[id].add)%p;
    tree[rson].mul = (tree[rson].mul*tree[id].mul)%p;
    tree[id].mul=1,tree[id].add=0;
}
void push_up(int id)
{
    tree[id].sum = (tree[id<<1].sum+tree[id<<1|1].sum)%p;
}
void build(int id,int l,int r)
{
    tree[id].l=l,tree[id].r=r;
    tree[id].mul = 1,tree[id].add = 0;
    if(l==r) {scanf("%d",&tree[id].sum);return ;}
    int mid = (l+r)>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    push_up(id);
}
void update(int id,int l,int r,int op,int k)
{
    if(tree[id].l==l && tree[id].r==r)
    {
        if(op==2)
        {
            tree[id].sum = (tree[id].sum+(r-l+1)*k)%p;
            tree[id].add = (tree[id].add+k)%p;
        }
        if(op==1)
        {
            tree[id].sum = (tree[id].sum*k)%p;
            tree[id].add = (tree[id].add*k)%p;
            tree[id].mul = (tree[id].mul*k)%p;
        }
        return ;
    }
    push_down(id);
    int mid = (tree[id].l+tree[id].r)>>1;
    if(r<=mid) update(id<<1,l,r,op,k);
    else if(l>mid) update(id<<1|1,l,r,op,k);
    else
    {
        update(id<<1,l,mid,op,k);
        update(id<<1|1,mid+1,r,op,k);
    }
    push_up(id);
}
ll query(int id,int l,int r)
{
    if(tree[id].l==l && tree[id].r==r)
    {
        return tree[id].sum%p;
    }
    push_down(id);
    int mid = (tree[id].l+tree[id].r)>>1;
    if(r<=mid) return query(id<<1,l,r)%p;
    else if(l>mid) return query(id<<1|1,l,r)%p;
    else return query(id<<1,l,mid)%p+query(id<<1|1,mid+1,r)%p;
}
int main()
{
    int c,l,r,x;
    scanf("%d%d%d",&n,&m,&p);
    build(1,1,n);
    while(m--)
    {
        scanf("%d",&c);
        if(c==3)
        {
            scanf("%d%d",&l,&r);
            printf("%lld\n",query(1,l,r)%p);
        }
        else
        {
            scanf("%d%d%d",&l,&r,&x);
            update(1,l,r,c,x);
        }
    }
    return 0;
}

HDU 4578 Transformation
这道题把区间加法、区间乘法和区间染色都放到了一起

值得注意的是,显然区间染色优先级最高,只要有染色标记开始打进来,add和mul首先要清空,也就是说add和mul有意义当且仅当他们在染色标记之后出现。

而且需要维护的不止是区间和,还有平方和和立方和

考虑到区间幂次的和,只有1,2,3 因此每一种用一个sum来维护

sum1是很容易得到的
那么sum2和sum3呢?
染色和乘法对于sum2和sum3的更新自然简单

加法的时候可以推一下公式
s u m 2 ′ = ∑ ( a i + k ) 2 = ∑ a i 2 + 2 ∗ ∑ a i ∗ k + ( r − l + 1 ) ∗ k 2 sum2' = ∑(ai+k)^{2} = ∑ai^2 + 2*∑ai*k + (r-l+1)*k^2 sum2=(ai+k)2=ai2+2aik+(rl+1)k2
化简之后就是
s u m 2 ′ = s u m 2 + 2 ∗ s u m 1 ∗ k + ( r − l + 1 ) ∗ k 2 sum2' = sum2+2*sum1*k+(r-l+1)*k^2 sum2=sum2+2sum1k+(rl+1)k2
即新的sum2可以由原来的sum2和sum1更新

s u m 3 ′ = ∑ ( a i + k ) 3 = ∑ a i 3 + 3 ∗ ∑ a i 2 ∗ k + 3 ∗ ∑ a i ∗ k 2 + ( r − l + 1 ) ∗ k 3 sum3' = ∑(ai+k)^3 = ∑ai^3+3*∑ai^2*k+3*∑ai*k^2+(r-l+1)*k^3 sum3=(ai+k)3=ai3+3ai2k+3aik2+(rl+1)k3
化简
s u m 3 ′ = s u m 3 + 3 ∗ s u m 2 ∗ k + 3 ∗ s u m 1 ∗ k 2 + ( r − l + 1 ) ∗ k 3 sum3' = sum3+3*sum2*k+3*sum1*k^2+(r-l+1)*k^3 sum3=sum3+3sum2k+3sum1k2+(rl+1)k3
解新的sum3可以由原来的sum3、sum2和sum1更新

要注意的是应该先更新sum3再更新sum2,最后才是sum1
因为要保证更新时用的都是原来的值(可以仔细体会一下)

//挺复杂的一道题
//包括 区间乘法 区间加法  区间染色  和  区间幂次查询

//前三个都好实现
//注意区间乘法和区间加法的混用————如何不考虑顺序
//每次区间乘法时  给加法的lazy也乘上  之后push_down的时候就可以先乘后加

//然后区间幂次的查询就比较技巧了
//由于幂次从1到3可知应该特殊处理三种情况
//即分成  s1,s2,s3三棵树
//然后考虑更新的问题   可以动手算一下发现规律
//具体看代码


#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn = 1e5+5;
const int mod = 10007;
int s1[maxn<<2],s2[maxn<<2],s3[maxn<<2],add[maxn<<2],mul[maxn<<2],change[maxn<<2];
void push_up(int id)
{
    s1[id] = (s1[id<<1]+s1[id<<1|1])%mod;
    s2[id] = (s2[id<<1]+s2[id<<1|1])%mod;
    s3[id] = (s3[id<<1]+s3[id<<1|1])%mod;
}
void push_down(int id,int l,int r)
{
    int mid = (l+r)>>1;
    int llen=mid-l+1,rlen=r-mid;
    int c1=add[id],c2=mul[id],c3=change[id];
    int &l1=s1[id<<1],&l2=s2[id<<1],&l3=s3[id<<1];
    int &r1=s1[id<<1|1],&r2=s2[id<<1|1],&r3=s3[id<<1|1];
    if(c3)
    {
        l1 = llen*c3%mod;
        l2 = llen*c3%mod*c3%mod;
        l3 = llen*c3%mod*c3%mod*c3%mod;
        r1 = rlen*c3%mod;
        r2 = rlen*c3%mod*c3%mod;
        r3 = rlen*c3%mod*c3%mod*c3%mod;
        add[id<<1] = add[id<<1|1] = 0;
        mul[id<<1] = mul[id<<1|1] = 1;
        change[id<<1] = change[id<<1|1] = c3;
    }
    if(c2>1)
    {
        l1 = l1*c2%mod;
        l2 = l2*c2%mod*c2%mod;
        l3 = l3*c2%mod*c2%mod*c2%mod;
        r1 = r1*c2%mod;
        r2 = r2*c2%mod*c2%mod;
        r3 = r3*c2%mod*c2%mod*c2%mod;
        add[id<<1] = add[id<<1]*c2%mod;
        add[id<<1|1] = add[id<<1|1]*c2%mod;
        mul[id<<1] = mul[id<<1]*c2%mod;
        mul[id<<1|1] = mul[id<<1|1]*c2%mod;
    }
    if(c1)
    {
        l3 = (l3+3*l2%mod*c1%mod+3*l1%mod*c1%mod*c1%mod+llen*c1%mod*c1%mod*c1%mod)%mod;
        r3 = (r3+3*r2%mod*c1%mod+3*r1%mod*c1%mod*c1%mod+rlen*c1%mod*c1%mod*c1%mod)%mod;
        l2 = (l2+2*l1%mod*c1%mod+llen*c1%mod*c1%mod)%mod;
        r2 = (r2+2*r1%mod*c1%mod+rlen*c1%mod*c1%mod)%mod;
        l1 = (l1+llen*c1%mod)%mod;
        r1 = (r1+rlen*c1%mod)%mod;
        add[id<<1] = (add[id<<1]+c1)%mod;
        add[id<<1|1] = (add[id<<1|1]+c1)%mod;
    }
    add[id] = 0;
    mul[id] = 1;
    change[id] = 0;
}
void build(int id,int l,int r)
{
    s1[id] = s2[id] = s3[id]=0;
    add[id] = change[id] = 0;
    mul[id] = 1;
    if(l==r) return ;
    int mid = (l+r)>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
}
void update(int id,int stl,int str,int l,int r,int op,int c)
{
    if(stl==l && str==r)
    {
        int len = str-stl+1;
        int &t1=s1[id],&t2=s2[id],&t3=s3[id];
        if(op==1)
        {
            t3 = (t3+3*t2%mod*c%mod+3*t1*c%mod*c%mod+c*c%mod*c%mod*len%mod)%mod;
            t2 = (t2+2*t1%mod*c%mod+c*c%mod*len%mod)%mod;
            t1 = (t1+len*c%mod)%mod;
            add[id] = (add[id]+c)%mod;
        }
        if(op==2)
        {
            t1 = t1*c%mod;
            t2 = t2*c%mod*c%mod;
            t3 = t3*c%mod*c%mod*c%mod;
            add[id] = add[id]*c%mod;
            mul[id] = mul[id]*c%mod;
        }
        if(op==3)
        {
            t1 = c*len%mod;
            t2 = c*c%mod*len%mod;
            t3 = c*c%mod*c%mod*len%mod;
            add[id] = 0;
            mul[id] = 1;
            change[id] = c;
        }
        return ;
    }
    push_down(id,stl,str);
    int mid = (stl+str)>>1;
    if(r<=mid) update(id<<1,stl,mid,l,r,op,c);
    else if(l>mid) update(id<<1|1,mid+1,str,l,r,op,c);
    else
    {
        update(id<<1,stl,mid,l,mid,op,c);
        update(id<<1|1,mid+1,str,mid+1,r,op,c);
    }
    push_up(id);
}
int query(int id,int stl,int str,int l,int r,int p)
{
    //printf("%d   %d   %d   %d    %d  %d  %d\n",stl,str,l,r,s1[id],s2[id],s3[id]);
    if(stl==l && str==r)
    {
        if(p==1) return s1[id]%mod;
        else if (p==2) return s2[id]%mod;
        else return s3[id]%mod;
    }
    push_down(id,stl,str);
    int mid = (stl+str)>>1;
    if(r<=mid) return query(id<<1,stl,mid,l,r,p)%mod;
    else if(l>mid) return query(id<<1|1,mid+1,str,l,r,p)%mod;
    else return (query(id<<1,stl,mid,l,mid,p)+query(id<<1|1,mid+1,str,mid+1,r,p))%mod;
}
int main()
{
    int n,m;
    int op,x,y,c;
    while(scanf("%d%d",&n,&m),n||m)
    {
        build(1,1,n);
        while(m--)
        {
            scanf("%d%d%d%d",&op,&x,&y,&c);
            if(op==4) printf("%d\n",query(1,1,n,x,y,c));
            else update(1,1,n,x,y,op,c%mod);
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值