HDU 4578 Transformation(线段树乱维护)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4578

解题思路:

各种标记,用到哪下放到哪。

肯定是能想到用各种标记维护的,关键是标记之间会相互影响。

现在有三个标记:

mtag维护乘法标记,初值为1

add维护加法标记,初值为0

isset维护区间是否用一个数覆盖

操作1,如果加法标记有数值,那么让它的值乘以乘法标记,这样下次计算时 先用掉乘法标记再用掉加法标记就不会错

操作2,正常执行

操作3,把加法乘法标记初始化为0和1


我们可以快速得到一次方的区间和,那么2次方,3次方怎么快速得到呢。

原来只要再维护两棵树就行了鸭!


有关区间修改和下放时如何根据各种标记更新区间和的问题(考数学公式喽)

假设维护三次方的和的数组为ttt[],二次为tt[],一次为t[]

标记下放前:a³+b³+c³+....

下放后每一项变为(a*x+y)

根据(a+b)³ = a³+b³+3ab²+3a²b

得出更新后ttt[] = ttt[]*x³ + (r-l+1)*y³+3t[]*y²+3*tt[]*y

由于ttt[]更新需要用到tt[],t[],所以这三棵树要从三次到一次倒着更新

同理,

二次方是tt[] = tt[]*x²+(r-l+1)*y²+2t[]xy

一次方是t[] = t[]*x + (r-l+1)*y


代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define mid int m=l+r>>1
#define debug(x) printf("----Line%s----\n",#x)
#define ls rt<<1
#define rs rt<<1|1
#define mod 10007
using namespace std;

const int N = 1e5+5;

ll add[N<<2],mtag[N<<2];
ll t[N<<2],tt[N<<2],ttt[N<<2];
ll isset[N<<2];

ll Pow(ll a,int b){
    if (b==2) return a*a%mod;
    if (b==3) return a*a%mod*a%mod;
}

void push_up(int rt)
{
    t[rt] = (t[ls] + t[rs])%mod;
    tt[rt] = (tt[ls] + tt[rs])%mod;
    ttt[rt] = (ttt[ls] + ttt[rs])%mod;
}

void push_down(int rt,int len)
{
    if (isset[rt]){
        ll x = isset[rt];
        isset[ls] = isset[rs] = isset[rt];
        isset[rt] = 0;
        mtag[ls] = mtag[rs] = 1;
        add[ls] = add[rs] = 0;
        ttt[ls] = (len-len/2)*Pow(x,3)%mod;
        tt[ls] = (len-len/2)*Pow(x,2)%mod;
        t[ls] = (len-len/2)*x%mod;
        ttt[rs] = (len/2)*Pow(x,3)%mod;
        tt[rs] = (len/2)*Pow(x,2)%mod;
        t[rs] = (len/2)*x%mod;
    }
    if (mtag[rt]==1 && add[rt]==0) return ;
    ttt[ls] = (ttt[ls]*Pow(mtag[rt],3)%mod + (len-len/2)*Pow(add[rt],3)%mod + 3*tt[ls]*Pow(mtag[rt],2)%mod*add[rt]%mod + 3*t[ls]*mtag[rt]%mod*Pow(add[rt],2)%mod)%mod;
    ttt[rs] = (ttt[rs]*Pow(mtag[rt],3)%mod +  len/2     *Pow(add[rt],3)%mod + 3*tt[rs]*Pow(mtag[rt],2)%mod*add[rt]%mod + 3*t[rs]*mtag[rt]%mod*Pow(add[rt],2)%mod)%mod;
    tt[ls] = (tt[ls]*Pow(mtag[rt],2)%mod + (len-len/2)*Pow(add[rt],2)%mod + 2*t[ls]*add[rt]*mtag[rt])%mod;
    tt[rs] = (tt[rs]*Pow(mtag[rt],2)%mod + len/2*Pow(add[rt],2)%mod       + 2*t[rs]*add[rt]*mtag[rt])%mod;
    t[ls] = (t[ls]*mtag[rt] + (len-len/2)*add[rt])%mod;
    t[rs] = (t[rs]*mtag[rt] + (len/2)*add[rt])%mod;
    mtag[ls] = (mtag[ls]*mtag[rt])%mod;
    mtag[rs] = (mtag[rs]*mtag[rt])%mod;
    add[ls] = (add[ls]*mtag[rt]%mod+add[rt])%mod;///这个标记也要先乘后加
    add[rs] = (add[rs]*mtag[rt]%mod+add[rt])%mod;
    add[rt] = 0;
    mtag[rt] = 1;
}

void update(int L,int R,int op,ll x,int rt,int l,int r)
{
    if (L<=l && r<=R){
        if (op==1){///+
            add[rt] = (add[rt]+x)%mod;
            ttt[rt] = (ttt[rt] + Pow(x,3)*(r-l+1)%mod + 3*tt[rt]*x%mod + 3*t[rt]*Pow(x,2)%mod)%mod;
            tt[rt] = (tt[rt] + Pow(x,2)*(r-l+1) + 2*t[rt]*x)%mod;
            t[rt] = (t[rt] + (r-l+1)*x)%mod;
        }
        if (op==2){///*
            mtag[rt] = (mtag[rt]*x)%mod;
            add[rt] = (add[rt]*x)%mod; ///这个是保证先乘后加结果正确的关键
            ttt[rt] = (ttt[rt]*Pow(x,3))%mod;
            tt[rt] = (tt[rt]*Pow(x,2))%mod;
            t[rt] = (t[rt]*x)%mod;
        }
        if (op==3){///覆盖
            mtag[rt] = 1,add[rt] = 0;
            ttt[rt] = (r-l+1)*Pow(x,3)%mod;
            tt[rt] = (r-l+1)*Pow(x,2)%mod;
            t[rt] = (r-l+1)*x%mod;
            isset[rt] = x;
        }
        return ;
    }
    mid;
    push_down(rt,r-l+1);
    if (L<=m) update(L,R,op,x,lson);
    if (R>m)  update(L,R,op,x,rson);
    push_up(rt);
}

ll query(int L,int R,ll x,int rt,int l,int r)
{
    if (L<=l && r<=R){
        if (x==1) return t[rt];
        if (x==2) return tt[rt];
        if (x==3) return ttt[rt];
    }
    push_down(rt,r-l+1);
    ll ans = 0;
    mid;
    if (L<=m) ans = (ans+query(L,R,x,lson))%mod;
    if (R>m)  ans = (ans+query(L,R,x,rson))%mod;
    return ans;
}

void build(int rt,int l,int r)
{
    add[rt] = isset[rt] = 0;///万恶的初始化,我放if里面了
    mtag[rt] = 1;
    if (l==r){
        t[rt] = tt[rt] = ttt[rt] = 0;

        return ;
    }
    mid;
    build(lson);
    build(rson);
    push_up(rt);
}

int main()
{
    int n,q;
    while (~scanf("%d %d",&n,&q),n||q){
        build(1,1,n);
        int op,l,r;
        ll x;
        while (q--){
            scanf("%d %d %d %lld",&op,&l,&r,&x);
            if (op!=4){
                update(l,r,op,x,1,1,n);
            }
            else
                printf("%lld\n",query(l,r,x,1,1,n)%mod);
        }
    }
    return 0;
}

总结:

1.了解了线段树区间操作各个标记同时出现时维护的方法。

2.线段树初始化还是建议不要用build了,干脆主函数搞个for(1~4*n)初始化得了。

 

 

 

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值