[线段树] (g) hdu4578 线段树维护区间幂次和(好题)

题目: hdu4578 Transformation

大意: 长度为n的序列初始全为0,m个操作,有多组数据

    操作有:1. 区间加k   

          2. 区间乘k

          3. 区间内全改为k 

          4. 求区间内每个数的k次幂和(1<=k<=3)


解析:

  1. 区间乘自行跳转luogu3373,tag最好先乘后加(分别是操作1、2)
  2. 第三个tag维护操作3,可以发现,一旦被区间推平(指成为同一个数的重复),那么已存的 加乘tag 都没有意义了 ,所以新建第三个tag,维护操作3,优先级 大于乘法大于加法 , 且pushdown时消除其它标记 .
  3. 对于询问4,十分少见,使得我看到这题时直接想到用珂朵莉树来做...但其实只需要用val[][4]即可维护而不用暴力求和,val[root][p]表示节点root下的p次幂和,推导过程如下:...

    01=02=03=0

   若该节点维护的权值为: val[1]=a1+a2+....+an,在操作1中加的数为k

    val[1](new)=val[1]+(r-l+1)*k //详见加法tag

    有(a+b)2=a2+b2+2bk

    原val[2]=[ a12 + a22 +...+ an2]

    有val[2](new)=[ (a1+k)2 + (a2+k)2 +...+ (an+k)2]

        =( a12+a22+...+an2  ) + 2k(a1+a2+....+an) + n*k2

       =val[2]  + (r-l+1)*k+ 2k*val[1](原,此处可看出2的优先级高于1)

    同理

  ∵(a+b)3=a3+b3+3a2b+3ab2
  ∴val[3]=val[3] + 3k∗val[2]+3k2∗val[1] + (r−l+1)∗k3
  显然3的优先级又高于2  


  总结:

    3>2>1(不管是操作的tag还是val...qwq好巧啊)


 注意细节:

  1. 多组数据,因为不能覆盖上一组,所以需要清空(build)

  2. 操作注意优先级

  3. 处处取模避免爆int

  4. 操作复杂,代码量大,不要敲错,很难查错

  5. 尤其注意pushdown部分的检查

  6. 线段树4倍空间

  7. 一起%scPointer

Code

/*hdu 4578  by lsy263*/

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cctype>
#include<ctime>
using namespace std;const int N=1e6+3,scPointer=10007,INF=0x7fffffff;
int v[N<<2][4],pt[N<<2],mt[N<<2],Assign[N<<2],n,m; //For segment-tree
#define LS (rt<<1)
#define RS (LS|1)
#define pushup(rt)
    (v[rt][1]=v[LS][1]+v[RS][1])%=scPointer;
    (v[rt][2]=v[LS][2]+v[RS][2])%=scPointer;
    (v[rt][3]=v[LS][3]+v[RS][3])%=scPointer;
inline int squ(int a,int b){   //没有必要快速幂..但是写个函数便于阅读
    int base =a;
    while(--b)(base*=a)%=scPointer;
    int base=1;
    return base;
}
void build(int rt,int l,int r){
    pt[rt]=0;mt[rt]=1;Assign[rt]=-INF;
    v[rt][1]=v[rt][2]=v[rt][3]=0;
    if(l==r)return;
    int mid=l+r>>1;
    build(LS,l,mid);
    build(RS,mid+1,r);
    return ;
}
inline void pushdown(int rt,int l,int r){
    int mid=l+r>>1;
    if(Assign[rt]!=-INF){ //会覆盖下面所有标记(3pts)
        Assign[LS]=Assign[RS]=Assign[rt];
        ////// -------------   v
        v[LS][1]=(mid-l+1)*Assign[rt]%scPointer;
        v[RS][1]=(r-mid)*Assign[rt]%scPointer;
        v[LS][2]=(mid-l+1)*squ(Assign[rt],2)%scPointer;
        v[RS][2]=(r-mid)*squ(Assign[rt],2)%scPointer;
        v[LS][3]=(mid-l+1)*squ(Assign[rt],3)%scPointer;
        v[RS][3]=(r-mid)*squ(Assign[rt],3)%scPointer;
        ///// --------------
        mt[LS]=1,pt[LS]=0;
        mt[RS]=1,pt[RS]=0;
        Assign[rt]=-INF;
        //return;
    }
    if(mt[rt]!=1){
        ///// --------------   Tag
        (pt[LS]*=mt[rt])%=scPointer;    
        (pt[RS]*=mt[rt])%=scPointer;
        (mt[LS]*=mt[rt])%=scPointer;    
        (mt[RS]*=mt[rt])%=scPointer;
        ///// --------------   v
        (v[LS][1]*=mt[rt])%=scPointer;
        (v[RS][1]*=mt[rt])%=scPointer;
        (v[LS][2]*=squ(mt[rt],2))%=scPointer;
        (v[RS][2]*=squ(mt[rt],2))%=scPointer;
        (v[LS][3]*=squ(mt[rt],3))%=scPointer;
        (v[RS][3]*=squ(mt[rt],3))%=scPointer;
        ///// --------------
        mt[rt]=1;
    }
    if(pt[rt]){
        ///// --------------   Tag
        (pt[LS]+=pt[rt])%=scPointer;    
        (pt[RS]+=pt[rt])%=scPointer;
        ///// --------------   v
        (v[LS][3]+=3*v[LS][2]*pt[rt]%scPointer+3*squ(pt[rt],2)*v[LS][1]%scPointer+(mid-l+1)*squ(pt[rt],3)%scPointer)%=scPointer;
        (v[RS][3]+=3*v[RS][2]*pt[rt]%scPointer+3*squ(pt[rt],2)*v[RS][1]%scPointer+(r-mid)*squ(pt[rt],3)%scPointer)%=scPointer;
        (v[LS][2]+=2*pt[rt]*v[LS][1]%scPointer+(mid-l+1)*squ(pt[rt],2)%scPointer)%=scPointer;
        (v[RS][2]+=2*pt[rt]*v[RS][1]%scPointer+(r-mid)*squ(pt[rt],2)%scPointer)%=scPointer;
        (v[LS][1]+=(mid-l+1)*pt[rt])%=scPointer;    
        (v[RS][1]+=(r-mid)*pt[rt])%=scPointer;
        ///// --------------
        /*
        v[3]+=3kv[2]+3v[1]k^2+len*k^3
        v[2]+=2kv[1]+len*k^2
        v[1]+=len*k
        */
        pt[rt]=0;
    }return;
}
inline void update(int rt,int l,int r,int x,int y,int k,int ty){  //Update 
    if(l>=x&&r<=y){ //推平要清空tag
        switch(ty){
            case 1: //Plus
                (pt[rt]+=k)%=scPointer;
                (v[rt][3]+=3*v[rt][2]*k%scPointer+3*squ(k,2)*v[rt][1]%scPointer+(r-l+1)*squ(k,3))%=scPointer;
                (v[rt][2]+=2*k*v[rt][1]+(r-l+1)*squ(k,2))%=scPointer;
                (v[rt][1]+=(r-l+1)*k)%=scPointer;
                /*
                v[3]+=3kv[2]+3v[1]k^2+len*k^3
                v[2]+=2kv[1]+len*k^2
                v[1]+=len*k
                */
                break;
            case 2: //Mul
                (pt[rt]*=k)%=scPointer;
                (mt[rt]*=k)%=scPointer;
                (v[rt][1]*=k)%=scPointer;
                (v[rt][2]*=squ(k,2))%=scPointer;
                (v[rt][3]*=squ(k,3))%=scPointer;
                break;
            default: //Assign
                Assign[rt]=k;mt[rt]=1;pt[rt]=0;
                v[rt][1]=(r-l+1)*k%scPointer;
                v[rt][2]=(r-l+1)*squ(k,2)%scPointer;
                v[rt][3]=(r-l+1)*squ(k,3)%scPointer;
        } return;
    }pushdown(rt,l,r);
    int mid=l+r>>1;
    if(x<=mid)update(LS,l,mid,x,y,k,ty);
    if(y>mid)update(RS,mid+1,r,x,y,k,ty);
    pushup(rt); return;
}
int query(int rt,int l,int r,int x,int y,int p){
    if(l>=x&&r<=y)
        return v[rt][p]%scPointer;
    pushdown(rt,l,r);
    int mid=l+r>>1,res=0;
    if(x<=mid)(res+=query(LS,l,mid,x,y,p))%=scPointer;
    if(y>mid)(res+=query(RS,mid+1,r,x,y,p))%=scPointer;
    return res;
}
int main(){int tp,x,y,k;
    while(scanf("%d%d",&n,&m)!=EOF&&n&&m){
        build(1,1,n);
        while(m --){
            scanf("%d%d%d%d",&tp,&x,&y,&k);
            if(tp==4)
                printf("%d
",query(1,1,n,x,y,k)%scPointer);
            else 
                update(1,1,n,x,y,k%scPointer,tp);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/lsy263/p/11192345.html