1. 程式人生 > 實用技巧 >luogu P4688 [Ynoi2016]掉進兔子洞

luogu P4688 [Ynoi2016]掉進兔子洞

bitset優化莫隊。

由於bitset並不能存可重集,所以我們考慮給每種元素在bitset裡留 \(k\) 個位置(\(k\) 為這種元素的個數)。我們只需要在離散化的時候不去重,然後把 \(p\) 放進bitset中第 \(p-cnt_p\) 個位置就行了(\(cnt_p\) 為bitset當前存的 \(p\) 的個數)。

發現數組開不下,我們把詢問分成三段處理就好。

程式碼:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<cmath>

using namespace std;

const int N=100009,M=34000;
int n,m,a[N],cnt=1,ans1[M],belong[M*3],block,b[N],sum[N];
bitset <N> now,ans[M];
struct Question
{
	int l,r,id;
	bool operator < (const Question A)const
	{
		if(belong[l]!=belong[A.l])
			return l<A.l;
		return (belong[l]&1)?r<A.r:r>A.r;
	}
}q[M*3];

int read()
{
	int x=0;
	char c=getchar();
	while(c<'0'||c>'9')
		c=getchar();
	while(c>='0'&&c<='9')
		x=x*10+c-'0',c=getchar();
	return x;
}

void init()
{
	n=read(),m=read();
	for (int i=1;i<=n;i++)
		a[i]=b[i]=read();
	sort(b+1,b+1+n);
	for (int i=1;i<=n;i++)
		a[i]=upper_bound(b+1,b+1+n,a[i])-b-1;
	block=(int)sqrt(N);
	for (int i=1;i<=(M-10)*3;i++)
		belong[i]=(i-1)/block+1;
}

void add(int x)
{
	now[x-sum[x]]=1;
	sum[x]++;
}

void del(int x)
{
	sum[x]--;
	now[x-sum[x]]=0;
}

void work()
{
	int tot=0,all=0;
	memset(ans1,0,sizeof(ans1));
	for (int i=1;i<=M-10&&cnt<=m;i++,cnt++,all++)
		tot++,q[tot].l=read(),q[tot].r=read(),ans1[i]+=q[tot].r-q[tot].l+1,q[tot].id=i,
		tot++,q[tot].l=read(),q[tot].r=read(),ans1[i]+=q[tot].r-q[tot].l+1,q[tot].id=i,
		tot++,q[tot].l=read(),q[tot].r=read(),ans1[i]+=q[tot].r-q[tot].l+1,q[tot].id=i;
	sort(q+1,q+1+tot);
	memset(sum,0,sizeof(sum));
	for (int i=1;i<=all;i++)
		ans[i].set();
	now.reset();
	int l=1,r=0;
	for (int i=1;i<=tot;i++)
	{
		while(r<q[i].r) add(a[++r]);
		while(l>q[i].l) add(a[--l]);
		while(r>q[i].r) del(a[r--]);
		while(l<q[i].l) del(a[l++]);
		ans[q[i].id]&=now;
	}
	for (int i=1;i<=all;i++)
		printf("%d\n",ans1[i]-ans[i].count()*3);
	if(cnt<=m)
		work();
}

int main()
{
	init();
	work();
	return 0;
}