30-Transformation(HDU4578)-区间线段树(复杂)

Posted zhumengdexiaobai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了30-Transformation(HDU4578)-区间线段树(复杂)相关的知识,希望对你有一定的参考价值。

http://acm.hdu.edu.cn/showproblem.php?pid=4578

            Transformation

Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 65535/65536 K (Java/Others)
Total Submission(s): 7556    Accepted Submission(s): 1918


Problem Description
Yuanfang is puzzled with the question below: 
There are n integers, a1, a2, …, an. The initial values of them are 0. There are four kinds of operations.
Operation 1: Add c to each number between ax and ay inclusive. In other words, do transformation ak<---ak+c, k = x,x+1,…,y.
Operation 2: Multiply c to each number between ax and ay inclusive. In other words, do transformation ak<---ak×c, k = x,x+1,…,y.
Operation 3: Change the numbers between ax and ay to c, inclusive. In other words, do transformation ak<---c, k = x,x+1,…,y.
Operation 4: Get the sum of p power among the numbers between ax and ay inclusive. In other words, get the result of axp+ax+1p+…+ay p.
Yuanfang has no idea of how to do it. So he wants to ask you to help him. 
 

 

Input
There 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.
 

 

Output
For 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
 
Source
 

思路:看题意可以很明显地看出是一道线段树题,也是一道很典型的线段树

题意相当明显,就不多说了,有三种操作,这三种操作顺序不一样的话得到的结果是不一样的,对于这种多操作的问题,我们要做的事尽量将多种操作合并成一种操作。

我们可将区间中的数看成是 ax+b的形式,对于加c操作,则变成 ax+b+c(b->b+c),对于乘c操作,则变成 acx+bc,(a->ac,b->bc)对于赋值c操作,则变成c,即(a->1,x->c,b->0),则我们可以在线段数中加以下标记,a,b,x分别是以上提到的变量,sum[3],表示答案,sum[0],sum[1],sum[2]分别表示1次方和,平方和,立方和。对于更新和查询操作我们用pushdown函数向下更新。对于维护平方和还有立方和的问题,我们只要将平方,立方展开再利用更新之前的值即可维护,具体方法这里不多说了

需要说明的是:每次取修改值,如果发现当前遍历区间完全在需要更新的区间内时,更新当前顶点,同时记录本次以及以前的修改操作,此时不再往后跟新。

后面pushdown时就是从这个节点读取修改信息更新后面的点。

关键代码处给出了推到公式:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 100010
#define mod 10007
#define mid ((t[p].l+t[p].r)>>1)
#define ls (p<<1)
#define rs (ls|1)
using namespace std;
struct tree
{
    int l,r;
    int a,b,c;  //a * x + b: a为乘法系数, b为需要加的值 ; c 放重置的值 
    int sum[3]; //三个次方的求和 
}t[maxn<<2];
void pushup(int p)
{
    int i;
    for(i=0;i<3;i++)
    t[p].sum[i]=(t[ls].sum[i]+t[rs].sum[i])%mod;
}
void pushdown(int p)
{
    if(t[p].l==t[p].r)
    return;
     int a=t[p].a,b=t[p].b,c=t[p].c;
     if(c) //需要重置 
     {
         t[ls].a=t[rs].a=a;
         t[ls].b=t[rs].b=b;
         t[ls].c=t[rs].c=c;
         //sum2 = k * (a * c + b)^2 //重置后每个数相同 
         t[ls].sum[2]=(t[ls].r-t[ls].l+1)*(a*c%mod+b)%mod*(a*c%mod+b)%mod*(a*c%mod+b)%mod;
         t[ls].sum[1]=(t[ls].r-t[ls].l+1)*(a*c%mod+b)%mod*(a*c%mod+b)%mod;
         t[ls].sum[0]=(t[ls].r-t[ls].l+1)*(a*c%mod+b)%mod;
         t[rs].sum[2]=(t[rs].r-t[rs].l+1)*(a*c%mod+b)%mod*(a*c%mod+b)%mod*(a*c%mod+b)%mod;
         t[rs].sum[1]=(t[rs].r-t[rs].l+1)*(a*c%mod+b)%mod*(a*c%mod+b)%mod;
         t[rs].sum[0]=(t[rs].r-t[rs].l+1)*(a*c%mod+b)%mod;
     }
     else //未重置,向下更新求和 
     {
         t[ls].a=(t[ls].a*a)%mod;
         t[ls].b=(t[ls].b*a+b)%mod; //先算乘法,再算加法,即:b = b * a + b‘; 
         t[rs].a=(t[rs].a*a)%mod;
         t[rs].b=(t[rs].b*a+b)%mod;
         //(av + b)^3 = a^3*v^3 + b^3 + 3a^2 * v^2 * b + 3av * b^2  //k为区间长度 
		 //		求和为=	a^3*sum2 + K * b^3 + 3a^2 * b * sum1 + 3a*b^2 * sum0 								 
         t[ls].sum[2]=(a*a%mod*a%mod*t[ls].sum[2]%mod+3*a%mod*a%mod*b%mod*t[ls].sum[1]%mod+3*a%mod*b%mod*b%mod*t[ls].sum[0]%mod+b*b%mod*b%mod*(t[ls].r-t[ls].l+1)%mod)%mod;
         //(av + b)^2=a^2*v^2 + b^2 + 2*a*b*v 求和:a^2*sum1 + b^2*k + 2ab*sum1  
		 t[ls].sum[1]=(a*a%mod*t[ls].sum[1]%mod+b*b%mod*(t[ls].r-t[ls].l+1)%mod+2*a*b%mod*t[ls].sum[0]%mod)%mod;
		 //av + b 求和:a*sum0 + b*k 
		 t[ls].sum[0]=(a*t[ls].sum[0]%mod+b*(t[ls].r-t[ls].l+1)%mod)%mod;
		 
         t[rs].sum[2]=(a*a%mod*a%mod*t[rs].sum[2]%mod+3*a%mod*a%mod*b%mod*t[rs].sum[1]%mod+3*a%mod*b%mod*b%mod*t[rs].sum[0]%mod+b*b%mod*b%mod*(t[rs].r-t[rs].l+1)%mod)%mod;
         t[rs].sum[1]=(a*a%mod*t[rs].sum[1]%mod+b*b%mod*(t[rs].r-t[rs].l+1)%mod+2*a*b%mod*t[rs].sum[0]%mod)%mod;
         t[rs].sum[0]=(a*t[rs].sum[0]%mod+b*(t[rs].r-t[rs].l+1)%mod)%mod;
     }
     t[p].b=t[p].c=0;
     t[p].a=1;
}
void build(int p,int l,int r)
{
    t[p].l=l,t[p].r=r;
    t[p].a=1,t[p].b=t[p].c=0;
    t[p].sum[0]=t[p].sum[1]=t[p].sum[2]=0; //全部初始化为0 
    if(l<r)
    {
        build(ls,l,mid);
        build(rs,mid+1,r);
    }
}
void change(int p,int l,int r,int val,int flag)
{
    if(t[p].l==l&&t[p].r==r)
    {
        if(flag==0)//加val 
        {
            t[p].b=(t[p].b+val)%mod;
            //y = (x + v)^3 //x为原来的值
			//y = x^3 + v^3 + 3 * x^2 * v + 3 * x * v^2;求和 = sum2 + v^3 + 3*sum1*v + 3*sum0*v^2
            t[p].sum[2]=(t[p].sum[2]+3*val%mod*t[p].sum[1]%mod+3*val%mod*val%mod*t[p].sum[0]%mod+val*val%mod*val%mod*(t[p].r-t[p].l+1)%mod)%mod;
            t[p].sum[1]=(t[p].sum[1]+val*val%mod*(t[p].r-t[p].l+1)%mod+2*val*t[p].sum[0]%mod)%mod;
            t[p].sum[0]=(t[p].sum[0]+val*(t[p].r-t[p].l+1))%mod;
        }
        else if(flag==1) //乘以val 
        {
            t[p].a=(t[p].a*val)%mod;
            t[p].b=(t[p].b*val)%mod;
            //y = (x * v)^3 = x^3 * v^3;求和 = v^3 * sum2 
            t[p].sum[2]=val*val%mod*val%mod*t[p].sum[2]%mod;
            t[p].sum[1]=val*val%mod*t[p].sum[1]%mod;
            t[p].sum[0]=val*t[p].sum[0]%mod;
        }
        else if(flag==2) //重置为val 
        {
            t[p].a=1;
            t[p].b=0;
            t[p].c=val;
			//y = (v)^3   
            t[p].sum[2]=(t[p].r-t[p].l+1)%mod*val%mod*val%mod*val%mod;
            t[p].sum[1]=(t[p].r-t[p].l+1)%mod*val%mod*val%mod;
            t[p].sum[0]=(t[p].r-t[p].l+1)*val%mod;
        }
        return;
    }
    pushdown(p);
    if(l>mid)
    change(rs,l,r,val,flag);
    else if(r<=mid)
    change(ls,l,r,val,flag);
    else
    {
        change(ls,l,mid,val,flag);
        change(rs,mid+1,r,val,flag);
    }
    pushup(p);
}
int query(int p,int l,int r,int flag)
{
    if(t[p].l==l&&t[p].r==r)
    return t[p].sum[flag];
    pushdown(p);
    if(l>mid)
    return query(rs,l,r,flag);
    else if(r<=mid)
    return query(ls,l,r,flag);
    else
    {
        return (query(ls,l,mid,flag)+query(rs,mid+1,r,flag))%mod;
    }
}
int main()
{
//    freopen("dd.txt","r",stdin);
    int n,m;
    while(scanf("%d%d",&n,&m)&&(n+m))
    {
        build(1,1,n);
        int q,x,y,c;
        while(m--)
        {
            scanf("%d%d%d%d",&q,&x,&y,&c);
            if(q==4)
            {
                printf("%d
",query(1,x,y,c-1));
            }
            else
            {
                change(1,x,y,c,q-1);
            }
        }
    }
    return 0;
}

  














以上是关于30-Transformation(HDU4578)-区间线段树(复杂)的主要内容,如果未能解决你的问题,请参考以下文章

HDU 4578 Transformation

HDU 4578 - Transformation - [加强版线段树]

hdu4578

HDU 4578(线段树

HDU - 4578 Transformation(线段树区间修改)

Transformation HDU - 4578