bzoj5042: LWD的分科岛 两种做法

Description

大家都知道在文理分科的时候总是让人纠结的,纠结的当然不只是自己。比如 YSY 就去读了文科, LWD 知道了很
气。于是他就去卡了 BZOJ 测评机, 晚上他做了一个谜一样的梦,自己在一座全是 YSY 的分科岛。这里有 YSY
草, YSY 花, YSY 糖……每个 YSY 都有一个美( Ti)丽( Zhong)值。当然没有小于零的体重啦!LWD 于是不
惜重金卖肉想买下这座岛,可是分科岛的岛主,是一位忠实的区间问题爱好者。他想把小岛传给一个谜一样的爱好
者,所以岛主给了 LWD 一个终极挑战——选出一片区域中最美丽的 YSY。可是岛主的审美观不像 YYR那么专一,
他有时喜欢现代美——最轻的,有时喜欢唐代美——最重的。这让被欢喜冲昏了头脑(血液集中在下半身)的 LWD
十分苦恼。他要在规定时间内完成挑战赢得买岛的权利,于是只有求助 DalaoYYR,可是 YYR 要准备课件啊。只
有比 YYR 弱很多的你能够帮他了。挑战内容如下岛主将把 N 个 YSY 摆成一个条形,并给出所有 YSY 的美丽值。
挑出多少个就要看岛主心情了,他觉得 LWD 是条汉子就会给出很多很多的 YSY 满足他。岛主将给出 Q 个考验,
询问内容是在给定区间内求出最美丽的 YSY。你已经了解规则了,开始你的表演吧!
Input

第一行为一个整数 N,表示岛主摆出了 N 个 YSY。
接下来一行 N 个整数,表示每个 YSY 的美丽值(单位:kg),因为 YSY 整体很美所以 YSY
不会超过 1019kg。
接下来一行一个整数 Q,表示岛主有 Q 个考验。Q<=1500000
接下来 Q 行,每行三个整数 opt, l, r 。 opt 等于 1 时表示岛主那时喜欢现代美,等于 2
时代表岛主那时喜欢唐代美。询问最美计划在区间[l, r]中进行
100%的数据 N<= 3000000, 美丽值不会超过 1019, Q<= 150000
数据量巨大,请优化读入
Output

每一行对于每个考验输出结果,以回车符结束。
Sample Input

5 5

1 2 3 4 5

1 1 2

2 1 3

1 2 5

2 4 5

2 1 5

Sample Output

1

3

2

5

5

题意

其实就是RMQ问题
支持最大最小值

题解

第一种做法 近似线性的rmq

这题巨强。。
带个log都不行。。
线段树什么的可以GG了。。
那么怎么做呢?
突破口就在允许离线
离线的话我们是有 O ( N + Q ) O(N+Q) O(N+Q)的做法的

下面来大致讲一下
首先,我们要先对序列建立笛卡尔树。。

性质
树中的元素满足二叉搜索树性质,要求按照中序遍历得到的序列为原数组序列
树中节点满足堆性质,节点的key值要大于(或小于)其左右子节点的key值

这就是笛卡尔树。。
那么这个有什么用呢?
我们可以发现,因为他的中序遍历就是原序列。。
并且还有堆的性质,其实两个点间的最大值就是他们LCA的值。。
然后我们就可以用TJ做到O(n+q)跑出答案了

那么怎么建笛卡尔树呢?

实际实现的时候,可以采用栈的数据结构。栈中保存当前树中的从树根开始的右子节点链,根在栈底部。 插入新元素的时候,从树的右子链的最末尾从下往上查找,直到找到第一个满足堆性质的节点(即找到的节点的key值大于当前需要插入的节点)。用栈来实现就是从栈顶不断弹出元素,直到栈顶的元素的key大于当前结点的key,然后将该节点入栈,同时将最后被弹出的节点的parent指向该节点,以及该节点的左子节点指向最后被弹出的节点。

其实我们建出的笛卡尔树并不需要十分标准。。左右儿子弄混了都没关系,反正我们要的是LCA

所以其实我们只需要建立一下他的父亲就好了。

具体看代码里面的Bt函数(其实我觉得上面的那段话已经很清楚了)

然后我们就是O(n+q)跑出rmq问题了,再也不用带log了

CODE:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=500005;
int n,m;
int a[N];
int fa[N];//父亲
int S[N];
struct qq
{
	int y,last;
}s[N];int num,last[N];
void init (int x,int y)
{
	num++;
	s[num].y=y;
	s[num].last=last[x];
	last[x]=num;
}
int root;
void Bt ()//建树 
{
	int top=1;S[1]=1;
	fa[1]=0;
	for (int u=2;u<=n;u++)
	{
		bool tf=false;
		int x=0,xx=0;
		while (top!=0)
		{
			x=S[top];
			if (a[x]>a[u])//就放在这里 
			{
				fa[u]=x;
				tf=true;
				break;
			}
			xx=x;
			top--;
		}
		fa[xx]=u;
		if (tf==false)//他是最大的
			fa[u]=0;
		S[++top]=u;
	}
	for (int u=1;u<=n;u++)
	{
		init(fa[u],u);
		if (fa[u]==0) root=u;
	}
}
struct qt
{
	int y,last,id;
}e[N*2];int last1[N];
void init1 (int x,int y,int id)
{
	num++;
	e[num].y=y;
	e[num].last=last1[x];
	last1[x]=num;
	e[num].id=id;
}
bool vis[N];//这个点访问过没
int f[N];
int LCA[N];
int find (int x){return f[x]==x?f[x]:f[x]=find(f[x]);}
void TJ (int x)
{
	vis[x]=true;
	f[x]=x;
	for (int u=last[x];u!=0;u=s[u].last)
	{
		int y=s[u].y;
		TJ(y);
		f[y]=x;
	}
	for (int u=last1[x];u!=0;u=e[u].last)
	{
		int y=e[u].y;
		if (vis[y]==false) continue;
		LCA[e[u].id]=find(y);
	}
	last[x]=0;
}
struct qo
{
	int l,r,id;
}Ask[N];int cnt=0;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void Bt1 ()//建树 
{
	int top=1;S[1]=1;
	fa[1]=0;
	for (int u=2;u<=n;u++)
	{
		bool tf=false;
		int x=0,xx=0;
		while (top!=0)
		{
			x=S[top];
			if (a[x]<a[u])//就放在这里 
			{
				fa[u]=x;
				tf=true;
				break;
			}
			xx=x;
			top--;
		}
		fa[xx]=u;
		if (tf==false)//他是最大的
			fa[u]=0;
		S[++top]=u;
	}
	for (int u=1;u<=n;u++)
	{
		init(fa[u],u);
		if (fa[u]==0) root=u;
	}
}
int main()
{
	num=0;
	n=read();m=read();
	for (int u=1;u<=n;u+=3)
		a[u]=read();
	Bt();
	num=0;
	for (int u=1;u<=m;u++)	
	{
		int op=read();
		if (op==1)//找最小值
			Ask[++cnt].l=read(),Ask[cnt].r=read(),Ask[cnt].id=u;
		else
		{
			int x=read(),y=read();
			init1(x,y,u);init1(y,x,u);
		}
	}
	TJ(root);
	
	for (int u=1;u<=n;u++) last1[u]=0,vis[u]=false;
	num=0;
	Bt1();
	num=0;
	for (int u=1;u<=cnt;u++)	init1(Ask[u].l,Ask[u].r,Ask[u].id),init1(Ask[u].r,Ask[u].l,Ask[u].id);
	TJ(root);

	for (int u=1;u<=m;u++) printf("%d\n",a[LCA[u]]);
	return 0;
}

第二个做法

之前写的那一个。。因为要对m进行r的排序。。
后来被我删掉了。。
今天发现一个很高妙的方法可以去掉排序
其实一点也不高妙
就是如果按r一个一个来做,可以用并查集+单调栈做到近似线性的复杂度
去掉排序的话,就是开个vector,把询问挂在点上。。
为什么以前这么多按照r排序一个一个做的题,我都没有想过这个写法呢QAQ
只要r不大的话,就可以类似桶排,把排序的log去掉了

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值