首页 > 其他 > 详细

Bzoj1798 Ahoi2009行星序列 双标记线段树

时间:2014-04-04 02:43:03      阅读:520      评论:0      收藏:0      [点我收藏+]

本来看见这个题第一反应是好水啊。。。


后来仔细想想好难啊因为乘法和加法的顺序是不能变的。。。


后来仔细想想才反应过来只需要双标记就可以了


这算是线段树双标记的经典题目了吧,对理解线段树的标记以及标记的下传有着很大的作用


两个标记加和乘

如果ax+b要乘一个数c就变成acx+bc

一直维护就好了


我对标记的理解是,当前节点的标记只是用于下穿才用的,换句话说,就是说当前节点的值已经算完了,标记的唯一作用就是下穿


理解好down函数!

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define MAX 100009
#define ll long long

using namespace std;

int n,m,mod,l[4*MAX],r[4*MAX];
ll he[4*MAX],add[4*MAX],mul[4*MAX];
int a[MAX];

void build(int x,int la,int ra)
{
	l[x]=la;
	r[x]=ra;
	add[x]=0;
	mul[x]=1;
	if(la==ra)
	{
		he[x]=a[la]%mod;
		return;
	}
	int mid=(la+ra)>>1;
	build(2*x,la,mid);
	build(2*x+1,mid+1,ra);
	he[x]=(he[2*x]+he[2*x+1])%mod;
	return;
}

void down(int x)
{
	if(mul[x]==1&&add[x]==0)
		return;
	int mid=(l[x]+r[x])>>1;
	mul[2*x]=(mul[2*x]*mul[x])%mod;
	mul[2*x+1]=(mul[2*x+1]*mul[x])%mod;
	add[2*x]=(add[2*x]*mul[x]+add[x])%mod;
	add[2*x+1]=(add[2*x+1]*mul[x]+add[x])%mod;
	he[2*x]=(he[2*x]*mul[x]+add[x]*(r[2*x]-l[2*x]+1))%mod;
	he[2*x+1]=(he[2*x+1]*mul[x]+(r[2*x+1]-l[2*x+1]+1)*add[x])%mod;
	mul[x]=1;
	add[x]=0;
}

void multiply(int x,int la,int ra,int num)
{
	if(l[x]>ra||r[x]<la)
		return;
	if(la<=l[x]&&r[x]<=ra)
	{
		mul[x]=(mul[x]*num)%mod;
		add[x]=(add[x]*num)%mod;
		he[x]=(he[x]*num)%mod;
		return;
	}
	if(l[x]==r[x])
		return;
	down(x);
	multiply(2*x,la,ra,num);
	multiply(2*x+1,la,ra,num);
	he[x]=(he[2*x]+he[2*x+1])%mod;
}

void Add(int x,int la,int ra,int num)
{
	if(l[x]>ra||r[x]<la)
		return;
	if(la<=l[x]&&r[x]<=ra)
	{
		add[x]=(add[x]+num)%mod;
		he[x]=(he[x]+(r[x]-l[x]+1)*num)%mod;
		return;
	}
	if(l[x]==r[x])
		return;
	down(x);
	Add(2*x,la,ra,num);
	Add(2*x+1,la,ra,num);
	he[x]=(he[2*x]+he[2*x+1])%mod;
	return;
}

long long ask(int x,int la,int ra)
{
	if(l[x]>ra||r[x]<la)
		return 0;
	if(la<=l[x]&&r[x]<=ra)
		return he[x]%mod;
	if(l[x]==r[x])
		return 0;
	down(x);
	return (ask(2*x,la,ra)+ask(2*x+1,la,ra))%mod;
}

int main()
{
	scanf("%d%d",&n,&mod);
	rep(i,1,n)
		scanf("%d",&a[i]);
	build(1,1,n);
	scanf("%d",&m);
	while(m--)
	{
		int control,a1,a2,a3;
		scanf("%d",&control);
		if(control==1)
		{
			scanf("%d%d%d",&a1,&a2,&a3),multiply(1,a1,a2,a3);
		}
		else
			if(control==2)
				scanf("%d%d%d",&a1,&a2,&a3),Add(1,a1,a2,a3);
			else
				scanf("%d%d",&a1,&a2),printf("%lld\n",ask(1,a1,a2)%mod);
	}
	return 0;
}

Bzoj1798 Ahoi2009行星序列 双标记线段树,布布扣,bubuko.com

Bzoj1798 Ahoi2009行星序列 双标记线段树

原文:http://blog.csdn.net/wbysr/article/details/22896779

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!