【bzoj4358】permu【XSY1535】seq(莫队+并查集)

考虑莫队,但是我们发现这个东东只支持 i n s ins ins(至于怎么支持等会再讲),不支持 d e l del del操作,所以我们构造一种只 i n s ins ins d e l del del的莫队。

由于我们按莫队的方法排序,第一关键字为 l l l所在的块,第二关键字为 r r r。所以当排完序后,肯定是当 l l l所在的块相同时, r r r单调递增,所以我们对 l l l所在的块相同的询问进行处理。设对于块 B B B,它的末尾位置为 e n d end end,我们先找出在排完序中的数组中所有 l l l所在的块为 B B B的询问 q 1 q_1 q1 q 2 q_2 q2…… q k q_k qk,那么这些询问肯定在数组中是连续的,而且它们的 r r r肯定单调递增。

然后这些询问肯定是长这样的:

在这里插入图片描述

既然 r r r单调递增,那么对于 r r r我们直接 i n s ins ins就好了。而对于 l l l,由于 l l l都在块 B B B里,所以我们每个询问直接从 e n d end end暴力向左 i n s ins ins就可以了。

至于 i n s ins ins操作,我们考虑用并查集维护。对于插入的每一个值 x x x,我们在值域中找到它,并把它分别和 x − 1 x-1 x1 x + 1 x+1 x+1所在并查集连起来(如果没有就不连),那么答案就是所有并查集中最大的 s i z e size size

代码如下:

#include<bits/stdc++.h>

#define N 50010

using namespace std;

struct Question
{
	int l,r,block,id;
}q[N];

int n,m,len,block,a[N],ed[N],ans,Ans[N],st[N],top;
int fa1[N],size[N];
int fa2[N],minn[N],maxn[N];

int find1(int x)
{
	return fa1[x]==x?x:fa1[x]=find1(fa1[x]);
}

int find2(int x)
{
	return fa2[x]==x?x:fa2[x]=find2(fa2[x]);
}

int get(int x)
{
	return (x-1)/len+1;
}

bool operator < (Question a,Question b)
{
	return a.block==b.block?a.r<b.r:a.l<b.l;
}

void add1(int u)
{
	int v;
	fa1[u]=u,size[u]=1;
	if(v=find1(u-1))
		fa1[v]=u,size[u]+=size[v];
	if(v=find1(u+1))
		fa1[v]=u,size[u]+=size[v];
	ans=max(ans,size[u]);
}

void add2(int u,int &Ans)
{
	int v;
	st[++top]=u;
	fa2[u]=u,minn[u]=maxn[u]=u;
	if(v=find2(u-1))minn[u]=minn[v];
	else if(v=find1(u-1)) minn[u]-=size[v];
	if(v=find2(u+1))maxn[u]=maxn[v];
	else if(v=find1(u+1)) maxn[u]+=size[v];
	Ans=max(Ans,maxn[u]-minn[u]+1);
	if(minn[u]!=u)fa2[minn[u]]=u,st[++top]=minn[u];
	if(maxn[u]!=u)fa2[maxn[u]]=u,st[++top]=maxn[u];
}

int main()
{
	scanf("%d%d",&n,&m);
	block=len=sqrt(n);
	while(block*len<n)block++;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1,edd=len;i<=block;i++,edd=min(edd+len,n))
		ed[i]=edd;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].block=get(q[i].l),q[i].id=i;
	}
	sort(q+1,q+m+1);
	for(int p=1,i=1;i<=m;i=p+1,p=i)
	{
		memset(fa1,0,sizeof(fa1));
		ans=0;
		while(q[i].block==q[p+1].block)p++;
		int now=ed[q[i].block];
		for(int j=i;j<=p;j++)
		{
			while(now<q[j].r)add1(a[++now]);
			Ans[q[j].id]=ans;
			for(int k=q[j].l;k<=min(q[j].r,ed[q[j].block]);k++)
				add2(a[k],Ans[q[j].id]);
			while(top)
				fa2[st[top--]]=0;
		}
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",Ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值