●线段树题之wows

●模拟考试的一道似乎是学长出的题,还不错,挺考代码能力的。以此记录。

(像手纸一样长的贴图……)

●题目大意:

有横向排布的n个点,每个点初始值都为0。

有m个操作(先输入操作类型com):

com==0:修改操作(接着输入l , r , a , k , p),解释见输入格式。(提供图帮助理解)。

com==1:查询操作(接着输入l , r),输出区间[ l , r ]内最长的等差数列的项数(要连续)。

●题解:

注意到是要找等差数列,即答案对应的区间个元素的差值应相等。即可以前后两元素的差值建线段树,然后维护区间最长的差值相等的一段。

(是不是思路很清晰,然而恶心的代码却搞了我大半个上午。)

●附上代码:

我上代码咯:(附上测试数据)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct segment{
	int l,r,nl,nr,mx,ml,mr,lazy;
}t[400005];
int n,m;
void pushdown(int u)
{
	t[u*2].lazy+=t[u].lazy; t[u*2].nl+=t[u].lazy; t[u*2].nr+=t[u].lazy;
	t[u*2+1].lazy+=t[u].lazy; t[u*2+1].nl+=t[u].lazy; t[u*2+1].nr+=t[u].lazy;
	t[u].lazy=0;
}
void pushup(int u)
{
	t[u].mx=max(t[u*2].mx,t[u*2+1].mx);
	t[u].ml=t[u*2].ml;
	t[u].mr=t[u*2+1].mr;
	t[u].nl=t[u*2].nl;
	t[u].nr=t[u*2+1].nr;
	if(t[u*2].nr==t[u*2+1].nl)
	{
		t[u].mx=max(t[u].mx,t[u*2].mr+t[u*2+1].ml);
		if(t[u*2].ml==(t[u*2].r-t[u*2].l+1)) t[u].ml+=t[u*2+1].ml;
		if(t[u*2+1].mr==(t[u*2+1].r-t[u*2+1].l+1)) t[u].mr+=t[u*2].mr;
	}
}
void build(int u,int l,int r)
{
	t[u]=(segment){l,r,0,0,r-l+1,r-l+1,r-l+1};
	if(l==r) return;
	int mid=(l+r)/2;
	build(u*2,l,mid);
	build(u*2+1,mid+1,r);
}
void add(int u,int l,int r,int x)
{
	if(l<=t[u].l&&t[u].r<=r)
	{
		t[u].nl+=x;
		t[u].nr+=x;
		t[u].lazy+=x;
		return;
	}
	if(t[u].lazy) pushdown(u);
	int mid=(t[u].l+t[u].r)/2;
	if(l<=mid) add(u*2,l,r,x);
	if(mid<r) add(u*2+1,l,r,x);
	pushup(u);
}
void query(int u,int l,int r,int &mx,int &lm,int &rm,int &ln,int &rn)
{
	int lmx=0,lml,lmr,lnl,lnr,rmx=0,rml,rmr,rnl,rnr;
	if(t[u].l==l&&t[u].r==r)
	{
		mx=t[u].mx; lm=t[u].ml; rm=t[u].mr; ln=t[u].nl; rn=t[u].nr;
		return;
	}
	if(t[u].lazy) pushdown(u);
	int mid=(t[u].l+t[u].r)/2;
	if(l<=mid) query(u*2,l,min(mid,r),lmx,lml,lmr,lnl,lnr);
	if(mid<r) query(u*2+1,max(mid+1,l),r,rmx,rml,rmr,rnl,rnr);
	if(lmx*rmx!=0)
	{
		mx=max(lmx,rmx);
		ln=lnl; rn=rnr; lm=lml; rm=rmr;
		if(lnr==rnl)
		{
			mx=max(mx,lmr+rml);
			if(lml==mid-l+1) lm+=rml;
			if(rmr==r-(mid+1)+1) rm+=lmr;
		}
	}
	else
	{
		if(lmx==0) swap(lmx,rmx),swap(lml,rml),swap(lmr,rmr),swap(lnl,rnl),swap(lnr,rnr);
		mx=lmx; lm=lml; rm=lmr; ln=lnl; rn=lnr;
	}
}
int main()
{
	freopen("wows.in","r",stdin);
	freopen("wows.out","w",stdout);
	scanf("%d%d",&n,&m);n--;
	build(1,1,n); int com;
	for(int i=1,l,r,a,k,p;i<=m;i++)
	{
		scanf("%d",&com);
		if(com==0)
		{
			scanf("%d%d%d%d%d",&l,&r,&a,&k,&p);
			if(l>1) add(1,l-1,l-1,a);
			if(p-1>=l) add(1,l,p-1,k);
			if(r-1>=p) add(1,p,r-1,-k);
			if(r<n) add(1,r,r,-(a+(2*p-l-r)*k));
		}
		if(com==1)
		{
			int ans,a1,a2,a3,a4;
			scanf("%d%d",&l,&r); r--;
			query(1,l,r,ans,a1,a2,a3,a4);
			printf("%d\n",ans+1);
		}
	}
	return 0;
}
时间: 2024-09-29 15:58:59

●线段树题之wows的相关文章

[luogu P3801] 红色的幻想乡 [线段树][树状数组]

题目背景 蕾米莉亚的红雾异变失败后,很不甘心. 题目描述 经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放. 我们将幻想乡看做是一个n*m的方格地区,一开始没有任何一个地区被红雾遮盖.蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行/整列,但不会影响到她所站的那个地区.如果两阵红雾碰撞,则会因为密度过大而沉降消失.灵梦察觉到了这次异变,决定去解决它.但在解决之前,灵梦想要了解一片范围红雾的密度.可以简述为两种操

poj 2777 count color 线段树

Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem. There is a very long board with length L centimeter, L is a positive integer, so we can evenly d

[题解]线段树专题测试2017.1.21

很单纯的一道线段树题.稍微改一下pushDown()就行了. Code(线段树模板竟然没超100行) 1 #include<iostream> 2 #include<sstream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<cctype> 8 #include<queue> 9

Codeforces 193 D. Two Segments(线段树)

机智的线段树题, 参考了这个题解http://www.cnblogs.com/keam37/p/4335914.html 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; #define lson(x) ((x<<1)+1) #define rson(x) ((x<<1)+2) const

A线段树

线段树专题 顾琪坤 1.简介: 打acm的时候,经常会碰到一类问题,比方给你n个数的序列,然后动态的更改某些数的值,然后又动态地询问某个区间的值的和或者其它乱七八糟的东西,对于单个更改或者询问,也许很轻松的就能写出O(n)的算法,但可能n比较大,可能有10的5次方的数量级,并且更改和询问的操作总次数q很多,q可能也是10的5次方的数量级,那么简单的写来的话,整个程序的复杂度是O(n*q),也就是10的10次方的数量级,这个复杂度在acm题目中是不可承受的,因为对于1s时限的题目,只能承受10的7

HDU3954 线段树(区间更新 + 点更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3954 , 一道比较好的线段树题,值得做. 题目是NotOnlySuccess大神出的,借此题来膜拜一下大神,毕竟我学的就是NotOnlySuccess线段树,ORZ. 这道题比较复杂,如何判断一波经验加成之后是否有英雄需要升级,如果升级需要如何处理,怎样维护Exp的区间最值,都是这道题的难点. 我在网上百度了别人的题解才收到的启发,具体方法如下: 线段树上三个数组:level[]表示等级的区间最值:

poj 2828 Buy Tickets 万能的线段树大法。

Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 14400   Accepted: 7199 Description Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue- The Lunar New Year wa

hdu4747(线段树区间更新)

Mex Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 1892    Accepted Submission(s): 625 Problem Description Mex is a function on a set of integers, which is universally used for impartial game

POJ 2828 Buy Tickets (线段树 单点更新 插队问题)

题目链接:http://poj.org/problem?id=2828 题意:有个家伙春节买票,闲的没事想了一个让我们做= =!,是酱紫的,有n个人,编号是val,将插队,其序号变为pos,编号的范围[0,n). 刚开始真心没有看出来是个线段树题,要不是在做线段树专题,打死我也不会向线段树上靠拢. 想想这是用线段树解决一个什么样的模型呢? 1:每个人有固定的位置,虽然中途在不停加入,但到最后,一个萝卜一个坑 2:是不停加入成员,而不是改变 3:用线段树记录空余位置 没想到用线段树是我遇到的第一个