[BZOJ4358]permu线段树+莫队

先放代码 晚上补(争取)


[BZOJ4358]permu 线段树+莫队做法

序列操作,多次询问,无修,标准的莫队。

在如何在不同区间内转移的问题上,我选择用线段树来维护(没听xfg讲回滚莫队不行啊)

其中,\(lm\)为一段序列左边界开始最长值域连续段,\(rm\)为右边界开始的,\(mm\)为整个区间中最长值域连续段。

类似于山 海 经\(pushup\)更新时,左仍为左,右仍为右;当左子区间的\(lm==len\),也就是左子区间全部连续时,这时原区间的\(lm=\)左子区间全部\(+\)右子区间的\(lm\);右边同理。

对于\(mm\),来源是左右子区间的\(mm\)和左子区间的\(rm\)与右子区间的\(lm\)的和的最大值。

因为我们开的是线段树,所以可以保证区间的连续性。

然后按上莫队板子就结束啦。

code:

#include<bits/stdc++.h>
#define fo(x,y,z) for(int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
typedef long long ll;
using namespace std;
inline int qr()
{
	char ch=getchar();int x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#define qr qr()
const int Ratio=0;
const int N=100005;
const int maxi=INT_MAX;
int n,m;
int sq,c[N],bl[N],ans[N],sj[N];
struct rmm
{
	int l,r,id;
}q[N];
bool cmp(rmm a,rmm b)
{
	if(bl[a.l]!=bl[b.l])
		return bl[a.l]<bl[b.l];
	if(bl[a.l]&1)
		return a.r<b.r;
	return a.r>b.r;
}
struct rmmm
{
	int len,lm,rm,mm;
}t[N<<2];
namespace Acheron
{
	#define lid (rt<<1)
	#define rid (rt<<1|1) 
	void Apushup(int rt)
	{
		t[rt].lm=t[lid].lm;
		t[rt].rm=t[rid].rm;
		if(t[lid].lm==t[lid].len)
			t[rt].lm+=t[rid].lm;
		if(t[rid].rm==t[rid].len)
			t[rt].rm+=t[lid].rm;
		t[rt].mm=max({t[lid].mm,t[rid].mm,t[lid].rm+t[rid].lm});
	}
	void Abuild(int rt,int l,int r)
	{
		if(l==r)
		{
			t[rt].len=1;
			return;
		}
		int mid=(l+r)>>1;
		Abuild(lid,l,mid);
		Abuild(rid,mid+1,r);
		t[rt].len=t[lid].len+t[rid].len;
		return;
	}
	void Aadd(int rt,int l,int r,int x)
	{
		if(l==x&&r==x)
		{
			t[rt].lm=t[rt].rm=t[rt].mm=1;
			return;
		}
		int mid=(l+r)>>1;
		if(x<=mid)
			Aadd(lid,l,mid,x);
		else
			Aadd(rid,mid+1,r,x);
		Apushup(rt);
		return;
	}
	void Adel(int rt,int l,int r,int x)
	{
		if(l==x&&r==x)
		{
			t[rt].lm=t[rt].rm=t[rt].mm=0;
			return;
		}
		int mid=(l+r)>>1;
		if(x<=mid)
			Adel(lid,l,mid,x);
		else
			Adel(rid,mid+1,r,x);
		Apushup(rt);
		return;
	}
}
int main()
{
	n=qr,m=qr;
	sq=sqrt(n);
	fo(i,1,n)
		c[i]=qr,bl[i]=(i-1)/sq+1;
	Acheron::Abuild(1,1,n);
	fo(i,1,m)
		q[i].l=qr,q[i].r=qr,q[i].id=i;
	sort(q+1,q+1+m,cmp);
//	Acheron::Abuild(1,1,n);
	fo(i,q[1].l,q[1].r)
	{
		sj[c[i]]++;
		Acheron::Aadd(1,1,n,c[i]);
	}
	ans[q[1].id]=t[1].mm;
	int ls=q[1].l,rs=q[1].r;
	fo(i,2,m)
	{
		while(ls>q[i].l)
		{
			ls--,sj[c[ls]]++; 
			if(sj[c[ls]]==1)
				Acheron::Aadd(1,1,n,c[ls]);
		}
		while(ls<q[i].l)
		{
			sj[c[ls]]--;
			if(sj[c[ls]]==0)
				Acheron::Adel(1,1,n,c[ls]);
			ls++;
		}
		while(rs<q[i].r)
		{
			rs++,sj[c[rs]]++;
			if(sj[c[rs]]==1)
				Acheron::Aadd(1,1,n,c[rs]);
		}
		while(rs>q[i].r)
		{
			sj[c[rs]]--;
			if(sj[c[rs]]==0)
				Acheron::Adel(1,1,n,c[rs]);
			rs--;
		}
		ans[q[i].id]=t[1].mm;
	}
	fo(i,1,m)
		printf("%d\n",ans[i]);
	return Ratio;
}
苍龙濯世

image


Updated On 4.23 更新了做法名称。

posted @ 2024-04-22 19:36  DrRatio  阅读(30)  评论(0编辑  收藏  举报