HDU4578(Transformation)

Transformation

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

思路

这道题看似是个简单的线段树实则是个扮猪吃老虎的题。先做的落谷3373再来做的这道题要好一点点。难点总共有几处:

  1. 前三个区间操作怎么进行?按什么顺序进行。首先都能看明白的是更改值的操作更优先,其次就是乘法操作要比加法操作优先,最后更新加法。

  2. p = 1,2,3次幂的答案怎么求?s1[k]表示一次方的求和,s2[k]表示二次方的求和,s3[k]表示三次方的求和。

  3. 对区间[l,r] 整体 + c的求和变形。最后会发现必须先更新s3[k] 再更新 s2[k] 最后更新s1[k]。

对区间[l,r] 整体 + c的求和变形。
s1[k] = (a1 + c) + (a2 + c) + (a3 + c) + .... + (an + c)
      = a1 + a2 + a3 + ... + an + (r-l+1) * c
      = s1[k] + (r-l+1)*c			//这一行的s1[k]是进行当前更新之前的值,下面同理
s2[k] = (a1 + c)^2 + (a2 + c)^2 + (a3 + c)^2 + .... + (an + c)^2
      = a1^2 + a2^2 + a3^2 + .... + an^2 + 2a1c + 2a2c + ... + 2anc + (r-l+1) * c^2
      = s2[k] + 2*c(a1+a2+a3+...+an) + (r-l+1) * c^2
      = s2[k] + 2*c*s1[k] + (r-l+1) * c^2
s3[k] = (a1 + c)^3 + (a2 + c)^3 + ... + (an + c)^3
      = a1^3 + 3*c^2*a1 + 3*c*a1^2 + c^3 + .... + an^3 + 3*c^2*an + 3*c*an^2 + c^3 
      = a1^3 + ... + an^3 + 3c(a1^2 + ... + an^2) + 3c^2(a1 + ... + an) + (r-l+1) * c^3
      = s3[k] + 3*c*s2[k] + 3*c^2 *s1[k] + (r-l+1) * c^3
add[k] += c;				//加法叠加
  1. 对区间[l,r] 整体 * c的求和变形。
s1[k] = a1*c + a2*c + a3*c + ... + an*c
      = (a1 + a2 + a3 +... + an)*c
      = s1[k] * c
s2[k] = (a1*c)^2 + (a2*c)^2 + ... +(an*c)^2
      = c^2(a1^2 + a2^2 + ... + an^2)
      = c^2*s2[k]
s3[k] = (a1*c)^3 + (a2*c)^3 + ... +(an*c)^3
      = c^3(a1^3 + a2^3 + ... + an^3)
      = c^3*s3[k]
add[k] = add[k] * c		//注意加法操作,比方说先做加法更新在做乘法更新那么根据分配率就可以可知
mul[k] = mul[k] * c		//乘法翻倍
  1. 对区间[l,r] 整体更改值 = c的求和变形,这个操作是最简单的,因为所有的叶节点的值都一样
s1[k] = (r-l+1) * c
s2[k] = (r-l+1) * c^2
s3[k] = (r-l+1) * c^3
mul[k] = 1			//取消乘法标记
add[k] = 0			//取消加法标记
res[k] = c

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
const int p = 10007;
#define lson k<<1,l,m
#define rson k<<1|1,m+1,r
#define ll long long
ll s[3][maxn<<2],add[maxn<<2];
ll res[maxn<<2],mul[maxn<<2];
inline int quickpow(ll a,ll b)			//快速幂 
{
	a = a%p;
	int ans = 1;
	while(b){
		if(b%2 == 1){
			ans = (ans * a)%p;
		}
		b >>= 1;
		a = (a * a)%p;
	}
	return ans;
}
inline void push_up(int k)
{
	s[0][k] = s[0][k<<1] + s[0][k<<1|1];
	s[1][k] = s[1][k<<1] + s[1][k<<1|1];
	s[2][k] = s[2][k<<1] + s[2][k<<1|1];
}
/*
//建不建树无所谓,用memset和fill初始化数组就好了,反正是一个空树
inline void build(int k,int l,int r)
{
	mul[k] = 1;
	add[k] = res[k] = 0;
	if(l == r){
		s[0][k] = s[1][k] = s[2][k] = 0;
		return ;
	}
	int m = (l + r) >> 1;
	build(lson);
	build(rson);
	push_up(k);
}
*/
inline void push_down(int k,int l,int r)
{
	int m = (l + r) >> 1;
	if(res[k]){				//重置 
		res[k<<1] = res[k<<1|1]	= res[k];
		add[k<<1] = add[k<<1|1] = 0;			//取消左右孩子的标记,自己理解
		mul[k<<1] = mul[k<<1|1] = 1;			//取消左右孩子的标记
		
		s[0][k<<1] = (ll)(m-l+1)*quickpow(res[k],1)%p;
		s[0][k<<1|1] = (ll)(r-m)*quickpow(res[k],1)%p;
		
		s[1][k<<1] = (ll)(m-l+1)*quickpow(res[k],2)%p;
		s[1][k<<1|1] = (ll)(r-m)*quickpow(res[k],2)%p;
		
		s[2][k<<1] = (ll)(m-l+1)*quickpow(res[k],3)%p;
		s[2][k<<1|1] = (ll)(r-m)*quickpow(res[k],3)%p;	
		res[k] = 0;
	}
	if(mul[k] != 1){		//乘法 
		mul[k<<1] = (ll)mul[k<<1] * quickpow(mul[k],1) % p;
		mul[k<<1|1] = (ll)mul[k<<1|1] * quickpow(mul[k],1) % p;
		//左右孩子的加法延迟翻倍叠加
		add[k<<1] = (ll)(add[k<<1] * quickpow(mul[k],1)) % p;
		add[k<<1|1] = (ll)(add[k<<1|1] * quickpow(mul[k],1)) % p;
		
		s[0][k<<1] = (ll)(s[0][k<<1] * quickpow(mul[k],1)) % p;
		s[0][k<<1|1] = (ll)(s[0][k<<1|1] * quickpow(mul[k],1)) % p;
		
		s[1][k<<1] = (ll)(s[1][k<<1] * quickpow(mul[k],2)) % p;
		s[1][k<<1|1] = (ll)(s[1][k<<1|1] * quickpow(mul[k],2)) % p;
		
		s[2][k<<1] = (ll)(s[2][k<<1] * quickpow(mul[k],3)) % p;
		s[2][k<<1|1] = (ll)(s[2][k<<1|1] * quickpow(mul[k],3)) % p;
		
		mul[k] = 1;
	}
	if(add[k]){				//加法 
		add[k<<1] = (ll)(add[k<<1] + add[k]) % p;
		add[k<<1|1] = (ll)(add[k<<1|1] + add[k]) % p;
		
		s[2][k<<1] = ((ll)s[2][k<<1] + (m-l+1)*quickpow(add[k],3)%p + 3*quickpow(add[k],1)*s[1][k<<1]%p + 3*quickpow(add[k],2)*s[0][k<<1]%p)%p; 
		s[2][k<<1|1] = ((ll)s[2][k<<1|1] + (r-m)*quickpow(add[k],3)%p + 3*quickpow(add[k],1)*s[1][k<<1|1]%p + 3*quickpow(add[k],2)*s[0][k<<1|1]%p)%p; 
		
		s[1][k<<1] = ((ll)s[1][k<<1] + 2*s[0][k<<1]*quickpow(add[k],1)%p + (m-l+1)*quickpow(add[k],2)%p)%p;
		s[1][k<<1|1] = ((ll)s[1][k<<1|1] + 2*s[0][k<<1|1]*quickpow(add[k],1)%p + (r-m)*quickpow(add[k],2)%p)%p;	
		
		
		s[0][k<<1] = ((ll)s[0][k<<1] + (m-l+1)*add[k]%p) % p;
		s[0][k<<1|1] = ((ll)s[0][k<<1|1] + (r-m)*add[k]%p) % p;	
		add[k] = 0;
	}
}
inline void update(int k,int l,int r,int ql,int qr,int ans,int op)
{
	if(qr < l || r < ql){
		return ;
	}
	if(ql <= l && r <= qr){
		if(op == 1){					//加法 
			s[2][k] = ((ll)s[2][k] + (r-l+1)*quickpow(ans,3)%p + 3*quickpow(ans,1)*s[1][k]%p + 3*quickpow(ans,2)*s[0][k]%p)%p; 
			s[1][k] = ((ll)s[1][k] + 2*s[0][k]*ans%p + (r-l+1)*quickpow(ans,2)%p)%p;
			s[0][k] = ((ll)s[0][k] + (r-l+1)*ans%p)%p;
			add[k] += ans;
		}
		if(op == 2){					//乘法 
			s[0][k] = (s[0][k] * quickpow(ans,1)) % p;
			s[1][k] = (s[1][k] * quickpow(ans,2)) % p;
			s[2][k] = (s[2][k] * quickpow(ans,3)) % p;
			add[k] = add[k] * ans % p;			 
			mul[k] = mul[k] * ans % p;
		}	
		if(op == 3){					//更改 
			s[0][k] = (ll)(r-l+1)*quickpow(ans,1)%p;
			s[1][k] = (ll)(r-l+1)*quickpow(ans,2)%p;
			s[2][k] = (ll)(r-l+1)*quickpow(ans,3)%p;
			res[k] = ans;				
			add[k] = 0;					//取消加法标记 
			mul[k] = 1; 				//取消乘法标记 
		} 
		return ;
	}
	push_down(k,l,r); 
	int m = (l+r)>>1;
	update(lson,ql,qr,ans,op);
	update(rson,ql,qr,ans,op);
	push_up(k);
}
inline ll query(int k,int l,int r,int ql,int qr,int op)
{
	if(qr < l || r < ql){
		return 0;
	}
	if(ql <= l && r <= qr){
		return s[op][k];
	}
	push_down(k,l,r);
	int m = (l+r)>>1;
	int s1 = query(lson,ql,qr,op)%p;
	int s2 = query(rson,ql,qr,op)%p;
	return (s1+s2)%p;
}
int main()
{
	int n,m;
	while(~scanf("%d%d",&n,&m)){
		if(n == 0 && m == 0)	break;
		memset(s,0,sizeof(s));
		memset(add,0,sizeof(add));
		memset(res,0,sizeof(res));
		fill(mul,mul+n+1,1);
	//	build(1,1,n);
		int c,x,y,z;
		while(m--){
			scanf("%d%d%d%d",&c,&x,&y,&z);
			if(c <= 3){
				update(1,1,n,x,y,z,c);	
			}
			else{
				ll ans = query(1,1,n,x,y,z-1);
				printf("%lld\n",ans);
			}
		} 
	}
	return 0;
}

愿你走出半生,归来仍是少年~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值