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

32 篇文章 0 订阅
31 篇文章 0 订阅

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


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


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


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


两个标记加和乘

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

一直维护就好了


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


down函数在整个程序中起了重要的作用


出这个题的原因是在省选级别的考试中有数据结构的题这是显然的,但是个人认为这个题的难度还可以在3h4t的考试中还算可以,不过我自己也承认,在考场上调出来是不容易的,记得在湖南的时候2个小时才写好类似的东西不过最后还算是对了。。


这个题没有什么奇怪的东西,只是最基础的线段树题目的简单拓展而已,写本题对于理解线段树的标记的作用以及标记的下传有着重要的意义

个人认为这算是比较简单的数据结构在本次考试中出现还算合适

#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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值