Transformation HDU - 4578 (线段树区间更新 多种操作)

Yuanfang is puzzled with the question below: 
There are n integers, a  1, a  2, …, a  n. The initial values of them are 0. There are four kinds of operations. 
Operation 1: Add c to each number between a  x and a  y inclusive. In other words, do transformation a  k<---a  k+c, k = x,x+1,…,y. 
Operation 2: Multiply c to each number between a  x and a  y inclusive. In other words, do transformation a  k<---a  k×c, k = x,x+1,…,y. 
Operation 3: Change the numbers between a  x and a  y to c, inclusive. In other words, do transformation a  k<---c, k = x,x+1,…,y. 
Operation 4: Get the sum of p power among the numbers between a  x and a  y inclusive. In other words, get the result of a  x  p+a  x+1  p+…+a  y  p
Yuanfang has no idea of how to do it. So he wants to ask you to help him. 
InputThere are no more than 10 test cases. 
For each case, the first line contains two numbers n and m, meaning that there are n integers and m operations. 1 <= n, m <= 100,000. 
Each the following m lines contains an operation. Operation 1 to 3 is in this format: "1 x y c" or "2 x y c" or "3 x y c". Operation 4 is in this format: "4 x y p". (1 <= x <= y <= n, 1 <= c <= 10,000, 1 <= p <= 3) 
The input ends with 0 0. 
OutputFor each operation 4, output a single integer in one line representing the result. The answer may be quite large. You just need to calculate the remainder of the answer when divided by 10007.Sample Input
5 5
3 3 5 7
1 2 4 4
4 1 5 2
2 2 5 8
4 3 5 3
0 0
Sample Output
307
7489


这道题有三种询问分别问区间和  平分的区间和  立方的区间和  有三种修改的方法 分别是 add 增加  mul 乘  sett 数值代替

所以要有三个lazy标记   当sett出现时  把 add  mul标记都取消    当mul 出现时 如果有add标记   add变为 add*mul

所以在pushdown的过程中 先sett 在mul  最后 add

对于 set 和 mul 来说 平分和 和 立方和比较好求 对于add来说

平方和这样来推:(a + c)2 = a2 + c2 + 2ac  , 即sum2[rt] = sum2[rt] + (r - l + 1) * c * c + 2 * sum1[rt] * c;

立方和这样推:(a + c)3 = a3 + c3 + 3a(a2 + ac) , 即sum3[rt] = sum3[rt] + (r - l + 1) * c * c * c + 3 * c * (sum2[rt] + sum1[rt] * c);

  几个注意点:add标记取消的时候是置0,mul标记取消的时候是置1;在PushDown()中也也要注意取消标记,如set操作中取消add和mul,mul操作中更新add; 在add操作中要注意sum3 , sum2 , sum1的先后顺序,一定是先sum3 , 然后sum2 , 最后sum1; int容易爆,还是用LL要保险一点; 

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ls rt<<1
#define rs rt<<1|1
#define maxn 100000+10
const ll mod=10007;
const int inf=0x3f3f3f3f;
using namespace std;
ll sum1[maxn<<2],sum2[maxn<<2],sum3[maxn<<2];//sum1区间和 sum2 平方和 sum3 立方和
ll add[maxn<<2],mul[maxn<<2],sett[maxn<<2];
void pushup(ll rt)
{
    sum1[rt]=(sum1[ls]+sum1[rs])%mod;
    sum2[rt]=(sum2[ls]+sum2[rs])%mod;
    sum3[rt]=(sum3[ls]+sum3[rs])%mod;
}
void build(ll l,ll r,ll rt) //建立树
{
    add[rt]=sett[rt]=0; //初始为0
    mul[rt]=1;//初始为1
    if(l==r)
    {
        sum1[rt]=sum2[rt]=sum3[rt]=0;
        return ;
    }
    ll m=(l+r)>>1;
    build(l,m,ls);
    build(m+1,r,rs);
    pushup(rt);
}
void pushdown(ll rt,ll ln,ll rn)//下推标记
{
    if(sett[rt]) //存在数值代替
    {
        sett[rs]=sett[ls]=sett[rt];
        add[ls]=add[rs]=0;
        mul[ls]=mul[rs]=1;
        ll tmp=sett[rt]%mod;
        sum1[ls]=((ln%mod)*tmp)%mod;
        sum1[rs]=((rn%mod)*tmp)%mod;

        tmp=(tmp*tmp)%mod;
        sum2[ls]=((ln%mod)*tmp)%mod;
        sum2[rs]=((rn%mod)*tmp)%mod;

        tmp=(tmp*(sett[rt]%mod))%mod;
        sum3[ls]=((ln%mod)*tmp)%mod;
        sum3[rs]=((rn%mod)*tmp)%mod;
        sett[rt]=0;
    }
    if(mul[rt]!=1)//mul不为1 说明有过2操作
    {
       mul[ls]=(mul[ls]*mul[rt])%mod;
       mul[rs]=(mul[rs]*mul[rt])%mod;
       if(add[ls])
            add[ls]=(add[ls]*mul[rt])%mod;
       if(add[rs])
            add[rs]=(add[rs]*mul[rt])%mod;

       ll tmp=mul[rt]%mod;
       sum1[ls]=sum1[ls]*tmp%mod;
       sum1[rs]=sum1[rs]*tmp%mod;

       tmp=tmp*tmp%mod;
       sum2[ls]=sum2[ls]*tmp%mod;
       sum2[rs]=sum2[rs]*tmp%mod;

       tmp=tmp*(mul[rt]%mod)%mod;

       sum3[ls]=sum3[ls]*tmp%mod;
       sum3[rs]=sum3[rs]*tmp%mod;
       mul[rt]=1;
    }
    if(add[rt])
    {
        add[ls]+=add[rt];
        add[rs]+=add[rt];
        ll tmp=((add[rt]%mod)*(add[rt]%mod))%mod*(add[rt]%mod)%mod;

        sum3[ls]=(sum3[ls]+((tmp*ln)%mod)+3*add[rt]*((sum2[ls]+(sum1[ls]*add[rt])%mod)%mod))%mod;
        sum3[rs]=(sum3[rs]+((tmp*rn)%mod)+3*add[rt]*((sum2[rs]+(sum1[rs]*add[rt])%mod)%mod))%mod;

        tmp=(add[rt]%mod)*(add[rt]%mod)%mod;
        sum2[ls]=(sum2[ls]+((tmp*ln)%mod)+2*((sum1[ls]*add[rt])%mod))%mod;
        sum2[rs]=(sum2[rs]+((tmp*rn)%mod)+2*((sum1[rs]*add[rt])%mod))%mod;

        sum1[ls]=(sum1[ls]+(add[rt]*ln)%mod)%mod;
        sum1[rs]=(sum1[rs]+(add[rt]*rn)%mod)%mod;
        add[rt]=0;
    }
}
void update(ll L,ll R,ll l,ll r,ll rt,ll c,ll x)
{
    if(L<=l&&r<=R)
    {
        if(x==3)
        {
            sett[rt]=c;
            mul[rt]=1;
            add[rt]=0;
            sum1[rt]=((r-l+1)*c)%mod;
            sum2[rt]=((r-l+1)*(c*c)%mod)%mod;
            sum3[rt]=((r-l+1)*(((c*c)%mod*c)%mod))%mod;
        }
        else if(x==2)
        {
            mul[rt]=(mul[rt]*c)%mod;
            if(add[rt])
                add[rt]=(add[rt]*c)%mod;
            sum1[rt]=(sum1[rt]*c)%mod;
            sum2[rt]=((sum2[rt]*c)%mod*c)%mod;
            sum3[rt]=(((sum3[rt]*c)%mod*c)%mod*c)%mod;
        }
        else if(x==1)
        {
            add[rt]+=c;
            ll tmp =(((c*c)%mod*c)%mod*(r-l+1))%mod;    //(r - l + 1) * c^3
            sum3[rt]=(sum3[rt]+tmp+3*c*((sum2[rt]+sum1[rt]*c)%mod))%mod;
            sum2[rt]=(sum2[rt]+(c*c%mod*(r-l+1)%mod)+2*sum1[rt]*c)%mod;
            sum1[rt]=(sum1[rt]+(r-l+1)*c)%mod;
        }
         return;
    }
    ll m=(l+r)>>1;
    pushdown(rt,m-l+1,r-m);
    if(L<=m) update(L,R,l,m,ls,c,x);
    if(R>m) update(L,R,m+1,r,rs,c,x);
    pushup(rt);

}
ll query(ll L,ll R,ll l,ll r,ll rt,ll c)
{
    if(L<=l&&r<=R)
    {
        if(c==1)
            return sum1[rt]%mod;
        else if(c==2)
            return sum2[rt]%mod;
        else
            return sum3[rt]%mod;
    }
    ll m=(l+r)>>1;
    pushdown(rt,m-l+1,r-m);
    ll ans=0;
    if(L<=m) ans+=query(L,R,l,m,ls,c);
    if(R>m)  ans+=query(L,R,m+1,r,rs,c);
    return ans%mod;
}
int main()
{
    ll l,r,c,x,n,m;
   while(~scanf("%lld%lld",&n,&m)&&n+m)
   {
       build(1,n,1);
       while(m--)
       {
           scanf("%lld%lld%lld%lld",&x,&l,&r,&c);
           if(x!=4)
            update(l,r,1,n,1,c,x);
           else
            printf("%lld\n",query(l,r,1,n,1,c));
       }
   }
}




你可能感兴趣的:(线段树)