bitset 优化莫队

发布时间 2023-09-05 21:39:11作者: 傻阙的缺

题目传送门:Ynoi跳进兔子洞

好题!

我们观察题目,发现题目让我们求的可以写成:

\[(r_1-l_1+1)+(r_2-l_2+1)+(r_3-l_3+1)-3\times size \]

其中:\(size\) 是三段中公共颜色的个数

问题转移成求三段公共颜色的个数。

考虑使用莫队,然后在转换左右边界的时候使用数组记录每个颜色出现几次,然后再求三个区间中每个颜色的最小值即可。

这显然是正确的,只是\(...\),优化吧

考虑只用一个变量记录出所有颜色分别出现几次,普通数组肯定是不行的,所以要用 \(bitset\)

但是问题来了,\(bitset\) 只能记录每个颜色是否出现,不能记录每个颜色出现几次

这个时候就不能转化了,因为无法转化,所以我们考虑运用人类智慧去使 \(bitset\) 可以做到。

我们发现转化左右边界的时候可以得到出这个颜色是第几次出现,那么我们再在离散化时做一下手脚,记录一下比这个颜色小的颜色有多少个,那么在增量时令位置 \(mn_i+co_i\) 变成一,其中 \(co_i\)\(i\) 颜色出现次数,\(mn_i\) 是比 \(i\) 小的颜色个数,显然这个位置是空的,最后再令在三段取交集,就是每个颜色的最小出现次数(理解一下)。

减去的时候也是同理。

这个时候就可以将六个量变成三组,每组两个量 \((l_i,r_i)\),分开求,大大降低时间复杂度

这个时候我们发现空间复杂度不够,用时间换空间,求三次就好了。

看代码吧:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+500;
int n,m,peo;
struct jgt
{
	int zhi,id;
}sr[N];
bool cmp(jgt t1,jgt t2)
{
	return t1.zhi<t2.zhi;
}
int a[N],b[N],sq,cnt,st[N];
struct jgt1
{
	int l,r,id;
}q[N];
bool cmp1(jgt1 t1,jgt1 t2)
{
	return (b[t1.l]^b[t2.l])?b[t1.l]<b[t2.l]:((b[t1.l]&1)?t1.r<t2.r:t1.r>t2.r);
//	return (b[t1.l]^b[t2.l])?b[t1.l]<b[t2.l]:t1.r<t2.r;
}
int idx,la;
bitset<N> ans[35000],now;
int ans1[N],tong[N];
void add(int wz)
{
	now.set(tong[a[wz]]+st[a[wz]],1);
	tong[a[wz]]++;
}
void del(int wz)
{
	tong[a[wz]]--;
	now.set(tong[a[wz]]+st[a[wz]],0);
}
void slove()
{
	sort(q+1,q+idx+1,cmp1);
	for(int i=1;i<=idx/3;i++) ans[i].set();
	int le=1,ri=0;
	now.reset();
	for(int i=1;i<=idx;i++)
	{
		while(ri<q[i].r) add(++ri);
		while(le>q[i].l) add(--le);
		while(ri>q[i].r) del(ri--);
		while(le<q[i].l) del(le++);
		ans[q[i].id-la]=(ans[q[i].id-la]&now);
	}
	for(int i=1;i<=idx;i++)
	ans1[q[i].id]-=ans[q[i].id-la].count();
	la=la+idx/3;
	while(ri>=le) del(ri--);
	idx=0;
}
int main()
{
	scanf("%d %d",&n,&m);
	sq=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&sr[i].zhi);
		sr[i].id=i;
		b[i]=(i-1)/sq+1;
	}
	sort(sr+1,sr+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		if(sr[i].zhi!=sr[i-1].zhi)
		{
			cnt++;
			st[cnt]=i;
		}
		a[sr[i].id]=cnt;
	}
	for(int i=1;i<=m;i++)
	{
		idx++;
		scanf("%d %d",&q[idx].l,&q[idx].r);
		q[idx].id=i;
		ans1[i]=ans1[i]+q[idx].r-q[idx].l+1;
		idx++;
		scanf("%d %d",&q[idx].l,&q[idx].r);
		q[idx].id=i;
		ans1[i]=ans1[i]+q[idx].r-q[idx].l+1;
		idx++;
		scanf("%d %d",&q[idx].l,&q[idx].r);
		q[idx].id=i;
		ans1[i]=ans1[i]+q[idx].r-q[idx].l+1;
		if(idx>=1e5) slove();
	}
	if(idx!=0) slove();
	for(int i=1;i<=m;i++)
	printf("%d\n",ans1[i]);
	return 0;
}