线段树之 HDU4578Transformation

这题,整整写了一天,公式不难推,但是加乘的先后顺序影响,整整debug了一天,而且是和ac了这题的队友一起找的。。

虽说这算基础题,但写起来确实很吃力,看来水平还不够,下面的解释也写得很含糊,有时候就有种只可意会不可言传的感觉。

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <string>
#include <sstream>
using namespace std;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long LL;
const double pi=4.0*atan(1.0);
const int MAXN=100005;
const int M=10007;
//	代表一次方		平方			立方
LL sumx1[MAXN<<2],sumx2[MAXN<<2],sumx3[MAXN<<2];
//	加				乘				替换	lazy标记
LL add[MAXN<<2],mul[MAXN<<2],tihuan[MAXN<<2];

void PushUp(int rt)
{
	sumx1[rt]=(sumx1[rt<<1]%M+sumx1[rt<<1|1]%M)%M;
	sumx2[rt]=(sumx2[rt<<1]%M+sumx2[rt<<1|1]%M)%M;
	sumx3[rt]=(sumx3[rt<<1]%M+sumx3[rt<<1|1]%M)%M;
}


void PushDown(int rt,int len)
{
	//必须先替换,后乘法,再加法
	if(tihuan[rt]!=-1)
	{
		tihuan[rt<<1]=tihuan[rt]%M;
		tihuan[rt<<1|1]=tihuan[rt]%M;
		sumx1[rt<<1]=tihuan[rt]%M*(len-(len>>1))%M;
		sumx1[rt<<1|1]=tihuan[rt]%M*(len>>1)%M;
		sumx2[rt<<1]=tihuan[rt]%M*tihuan[rt]%M*(len-(len>>1))%M;
		sumx2[rt<<1|1]=tihuan[rt]%M*tihuan[rt]%M*(len>>1)%M;
		sumx3[rt<<1]=tihuan[rt]%M*tihuan[rt]%M*tihuan[rt]%M*(len-(len>>1))%M;
		sumx3[rt<<1|1]=tihuan[rt]%M*tihuan[rt]%M*tihuan[rt]%M*(len>>1)%M;

		tihuan[rt]=-1;
		//这里坑了一天,首先如果替换了,那么当前的add[rt],mul[rt]里面的值不能修改!
		//可能会有疑惑,不是说替换就可以去掉加法和乘法的影响吗,确实是,但是不是在这里,此时如果add[rt],mul[rt]有值就代表先发生替换,后发生加乘
		//发生先发生加乘后发生替换的话,在update时候就已经将add[rt],mul[rt]清空
		//这里发生替换,需要将孩子节点的add[rt],mul[rt]清空
		add[rt<<1]=0;
		add[rt<<1|1]=0;
		mul[rt<<1]=1;
		mul[rt<<1|1]=1;
	}
	LL x=1;
	if(mul[rt]!=1)
	{
		LL c=mul[rt];
		//这里修改add,原理同update函数里的解释
		add[rt<<1]=add[rt<<1]*c%M;
		add[rt<<1|1]=add[rt<<1|1]*c%M;
		
		mul[rt<<1]=(mul[rt<<1]%M*mul[rt]%M)%M;
		mul[rt<<1|1]=(mul[rt<<1|1]%M*mul[rt]%M)%M;
		sumx3[rt<<1]=(sumx3[rt<<1]%M*c%M*c%M*c%M)%M;
		sumx2[rt<<1]=(sumx2[rt<<1]%M*c%M*c%M)%M;
		sumx1[rt<<1]=(sumx1[rt<<1]%M*c%M)%M;
		sumx3[rt<<1|1]=(sumx3[rt<<1|1]%M*c%M*c%M*c%M)%M;
		sumx2[rt<<1|1]=(sumx2[rt<<1|1]%M*c%M*c%M)%M;
		sumx1[rt<<1|1]=(sumx1[rt<<1|1]%M*c%M)%M;
		mul[rt]=1;
	}
	if(add[rt])
	{
		LL c=add[rt]%M;
		add[rt<<1]=(add[rt<<1]+c)%M;
		add[rt<<1|1]=(add[rt<<1|1]+c)%M;
		sumx3[rt<<1]=(sumx3[rt<<1]+(3*c%M*c%M*sumx1[rt<<1]%M+3*c*sumx2[rt<<1]%M)%M+(c%M*c%M*c%M)%M*(len-(len>>1)))%M;
		sumx2[rt<<1]=(sumx2[rt<<1]+2*c%M*sumx1[rt<<1]%M+(c%M*c%M)*(len-(len>>1))%M)%M;
		sumx1[rt<<1]=(sumx1[rt<<1]+c*(len-(len>>1))%M)%M;
		sumx3[rt<<1|1]=(sumx3[rt<<1|1]+(3*c%M*c%M*sumx1[rt<<1|1]%M+3*c*sumx2[rt<<1|1]%M)+(c*c*c)*(len>>1))%M;
		sumx2[rt<<1|1]=(sumx2[rt<<1|1]+(2*c%M*sumx1[rt<<1|1])+(c*c)*(len>>1)%M)%M;
		sumx1[rt<<1|1]=(sumx1[rt<<1|1]+c*(len>>1)%M)%M;
		add[rt]=0;
	}


}
void build(int l,int r,int rt)
{
	add[rt]=0;
	mul[rt]=1;
	tihuan[rt]=-1;
	if(l==r)
	{
		sumx1[rt]=0;
		sumx2[rt]=0;
		sumx3[rt]=0;
		return ;
	}
	int m=(l+r)>>1;
	build(lson);
	build(rson);
	PushUp(rt);
}
void update(int kind,int L,int R,LL c,int l,int r,int rt)
{
	if(L<=l &&r<=R)
	{
		if(kind==1)//加法
		{
			//跟平时遇到的累加一样
			add[rt]=(add[rt]+c)%M;//lazy标记add累加

			sumx3[rt]=(sumx3[rt]+(3*c%M*c%M*sumx1[rt]%M+3*c*sumx2[rt]%M)+c*c%M*c%M*(r-l+1))%M;
			sumx2[rt]=(sumx2[rt]+(2*c%M*sumx1[rt])+(c*c%M)*(r-l+1)%M)%M;
			sumx1[rt]=(sumx1[rt]+c*(r-l+1)%M)%M;
			return ;
		}
		if(kind==2)//乘法
		{
			//这里是重点,首先因为加法和乘法同时存在,所以两者之间的顺序关系会影响到结果
			//当前是乘法,那么add标记里的值也要乘上该数字,否则我们必须要考虑加法和乘法的顺序,这样会相当复杂
			//比如(x+3)*4
			//先前add[rt]=3,此时我们进行*4,如果单单mul[rt]*=4,那么更新孩子节点时,就会发现不对了,更新成了x*4+3
			//实际上(x+3)*4=4*x+12
			//好吧,我也讲不清楚了
			add[rt]=add[rt]*c%M;
			mul[rt]=(mul[rt]*c)%M;
			sumx3[rt]=(sumx3[rt]*c%M*c%M*c%M)%M;
			sumx2[rt]=(sumx2[rt]*c%M*c%M)%M;
			sumx1[rt]=(sumx1[rt]*c%M)%M;
			return ;

		}
		if(kind==3)//替换
		{
			//替换的话,加法和乘法的标记都可以清空了
			tihuan[rt]=c%M;
			sumx1[rt]=c%M*(r-l+1)%M;
			sumx2[rt]=c%M*c%M*(r-l+1)%M;
			sumx3[rt]=c%M*c%M*c%M*(r-l+1)%M;
			add[rt]=0;
			mul[rt]=1;
			return ;

		}	
	}

	PushDown(rt,r-l+1);

	int m=(l+r)>>1;

	if(L<=m)
		update(kind,L,R,c,lson);
	if(m<R)
		update(kind,L,R,c,rson);

	PushUp(rt);
}
LL ans;
void query(int L, int R,int kind,int l,int r,int rt)
{
	if(L<=l &&r<=R)
	{
		if(kind==1)
			ans=(ans+sumx1[rt]%M)%M;
		else if(kind==2)
			ans=(ans+sumx2[rt]%M)%M;
		else if(kind==3)
			ans=(ans+sumx3[rt]%M)%M;
		return;
	}
	PushDown(rt,r-l+1);

	int m=(l+r)>>1;

	if(L<=m)
		query(L,R,kind,lson);
	if(m<R)
		query(L,R,kind,rson);
}
int main()
{
	int n,m;
	int i,j,k;
	int x,y,z;
	int t;

	while(scanf("%d%d",&n,&m)!=EOF)
	{
		if(n==0&&m==0)
			return 0;

		build(1,n,1);
		int cnt=0;
		while(m--)
		{
			scanf("%d%d%d%d",&t,&x,&y,&z);
			if(t!=4)
				update(t,x,y,(LL)z,1,n,1);
			else
			{
				ans=0;
				query(x,y,z,1,n,1);
				ans%=M;
				printf("%I64d\n",ans);
			}
		}
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值