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
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.
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.
思路:看题意可以很明显地看出是一道线段树题,也是一道很典型的线段树
题意相当明显,就不多说了,有三种操作,这三种操作顺序不一样的话得到的结果是不一样的,对于这种多操作的问题,我们要做的事尽量将多种操作合并成一种操作。
我们可将区间中的数看成是 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 - [加强版线段树]