HDU4578——Transformation(线段树)

又是一道很裸的线段树,一个数列a1, a2, …, an,然后4种操作:

1,x,y,c:将ax到ay之间的数都加c

2,x,y,c:将ax到ay之间的数都乘以c

3,x,y,c:将ax到ay之间的数变成c

4,x,y,p:求出ax到ay之间数字的p次幂的和,p=1,2,3,结果对10007取模

首先肯定要先保存3个次幂的和,然后对于每个结点用a和b作标记,f表示需要pushdown,则每个数i可以表示成ai+b

把上面三个操作做个统一,就是输入给一组(j,k),计算j(ai+b)+k=j*ai + j*b+k,并将系数重新赋值给a和b

三种操作的参数如下:

1:(1,c)

2:(c,0)

3:(0,c)

然后是传入(j,k)三个和的变化,这个公式也不难推导,但要注意计算的顺序应该是从高次往低次更新,因为高次的计算总要利用到低次的结果

每次pushdown后a=1,b=0,f=0

剩下就只是实现上的细节了

PS:因为各种脑残手抽WA了几次才A,以后还是要注意细节方面的问题

//HDU 4578 3671MS 9952K 2560B G++
//Author: hongrock
#include<cstdio>
#include<cstring>
#define LEFT(a) ((a)<<1)
#define RIGHT(a) (((a)<<1)|1)
#define MID(a,b) (((a)+(b))>>1) 
#define MAXN 400010
#define MOD 10007
int n, q, op, x, y, c, s, l[MAXN], r[MAXN], a[MAXN], b[MAXN], sum[MAXN][3];
bool f[MAXN];
char ch;
int getnum(){
	s=0;
	ch=getchar();
	while(ch<48 || ch>57)	ch=getchar();
	while(ch>=48 && ch<=57){
		s = s*10+ch-48;
		ch = getchar();
	}
	return s;
}
void build(int o, int ll, int rr){
	l[o]=ll;
	r[o]=rr;
	a[o]=1;
	if(ll<rr){
		int m = MID(ll,rr);
		build(LEFT(o), ll, m);
		build(RIGHT(o), m+1, rr);
	}
}
void maintain(int o){
	for(int i=0; i<3; i++)	sum[o][i] = (sum[LEFT(o)][i]+sum[RIGHT(o)][i])%MOD;
}
void pushdown(int o);
void update(int o, int ll, int rr, int j, int k){
	if(l[o]==ll && r[o]==rr){
		int j1,j2,j3,k1,k2,k3;
		j1=j;
		j2=(j*j1)%MOD;
		j3=(j*j2)%MOD;
		k1=k;
		k2=(k*k1)%MOD;
		k3=(k*k2)%MOD;
		sum[o][2] = (j3*sum[o][2])%MOD + (((j2*k1)%MOD*3)%MOD*sum[o][1])%MOD + (((j1*k2)%MOD*3)%MOD*sum[o][0])%MOD + ((rr-ll+1)*k3)%MOD;
		sum[o][2]%=MOD;
		sum[o][1] = (j2*sum[o][1])%MOD + (((j1*k1)%MOD*2)%MOD*sum[o][0])%MOD + ((rr-ll+1)*k2)%MOD;
		sum[o][1]%=MOD;
		sum[o][0] = (j1*sum[o][0])%MOD + ((rr-ll+1)*k1)%MOD;
		sum[o][0]%=MOD;
		a[o] = (a[o]*j)%MOD;
		b[o] = ((j*b[o])%MOD + k)%MOD;
		f[o] = 1;
	}
	else{
		if(f[o])	pushdown(o);
		int m = MID(l[o],r[o]);
		if(m<ll)	update(RIGHT(o), ll, rr, j, k);
		else if(m>=rr)	update(LEFT(o), ll, rr, j, k);
		else{
			update(LEFT(o), ll, m, j, k);
			update(RIGHT(o), m+1, rr, j, k);
		}
		maintain(o);
	}
}
void pushdown(int o){
	int m = MID(l[o],r[o]);
	update(LEFT(o), l[o], m, a[o], b[o]);
	update(RIGHT(o), m+1, r[o], a[o], b[o]);
	a[o]=1;
	b[o]=0;
	f[o]=0;
}
int query(int o, int ll, int rr, int p){
	if(l[o]==ll && r[o]==rr)	return sum[o][p];
	if(f[o])	pushdown(o);
	int tmp, m=MID(l[o], r[o]);
	if(m<ll)	tmp = query(RIGHT(o), ll, rr, p);
	else if(m>=rr)	tmp = query(LEFT(o), ll, rr, p);
	else{
		tmp = (query(LEFT(o), ll, m, p)+query(RIGHT(o), m+1, rr, p))%MOD;
	}
	maintain(o);
	return tmp;
}
int main(){
	while(1){
		n=getnum();
		q=getnum();
		if(!(n||q))	break;
		memset(sum, 0, sizeof(sum));
		memset(b, 0, sizeof(b));
		memset(f, 0, sizeof(f));
		build(1, 1, n);
		while(q--){
			op=getnum();
			x=getnum();
			y=getnum();
			c=getnum();
			if(op==1)	update(1, x, y, 1, c);
			else if(op==2)	update(1, x, y, c, 0);
			else if(op==3)	update(1, x, y, 0, c);
			else	printf("%d\n", query(1, x, y, c-1));
		}
	} 
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值