【洛谷P4867】Gty的二逼妹子序列

题目

题目链接:https://www.luogu.com.cn/problem/P4867

Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题。

对于一段妹子们,他们想让你帮忙求出这之内美丽度\(\in [a,b]\)的妹子的美丽度的种类数。

为了方便,我们规定妹子们的美丽度全都在\([1,n]\)中。
给定一个长度为\(n(1 \le n \le 100000)\)的正整数序列\(s(1 \le si \le n)\),对于\(m(1 \le m \le 1000000)\)次询问l,r,a,b,每次输出\(s_l \cdots s_r\)中,权值\(\in [a,b]\)的权值的种类数。

思路

如果没有 \(a,b\) 的约束,随便搞一个莫队就可以了。
但是有 \(a,b\) 的限制之后,我们统计答案的时候如果暴力枚举 \([a,b]\) 的数值复杂度显然是 \(O(nm)\),如果用树状数组求区间和的话,莫队的端点每移动一次都会多出 \(O(\log n)\) 的复杂度,总复杂度是 \(O(n^{\frac{3}{2}}\log n)\),不可接受。
发现统计答案只有 \(O(m)\) 次,所以我们可以考虑一个修改 \(O(1)\),查询 \(O(\sqrt{n})\) 的做法。值域分块一下,每次 \([a,b]\) 就转换为了左右的零散区间和中间的完整区间。
时间复杂度 \(O(m\sqrt{n})\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=100010,M=1000010;
int n,m,T,ans[M],a[N],cnt[N],sum[N],bel[N];

struct node
{
	int l,r,a,b,id;
}ask[M];

bool cmp(node x,node y)
{
	return bel[x.l]==bel[y.l] ? x.r<y.r : bel[x.l]<bel[y.l];
}

void add(int x)
{
	if (!cnt[x]) sum[bel[x]]++;
	cnt[x]++;
}

void del(int x)
{
	cnt[x]--;
	if (!cnt[x]) sum[bel[x]]--;
}

int calc(int l,int r)
{
	int s=0;
	for (int i=l;i<=r;i++)
		s+=(cnt[i]>0);
	return s;
}

int main()
{
	scanf("%d%d",&n,&m);
	T=sqrt(n)+1;
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		bel[i]=(i-1)/T+1;
	}
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&ask[i].l,&ask[i].r,&ask[i].a,&ask[i].b);
		ask[i].id=i;
	}
	sort(ask+1,ask+1+m,cmp);
	for (int i=1,l=1,r=0;i<=m;i++)
	{
		for (;l>ask[i].l;l--) add(a[l-1]);
		for (;r>ask[i].r;r--) del(a[r]);
		for (;r<ask[i].r;r++) add(a[r+1]);
		for (;l<ask[i].l;l++) del(a[l]);
		int p=(ask[i].a-1)/T+1,q=(ask[i].b-1)/T+1,id=ask[i].id;
		if (p==q) ans[id]=calc(ask[i].a,ask[i].b);
		else
		{
			ans[id]=calc(ask[i].a,T*p)+calc(T*(q-1)+1,ask[i].b);
			for (int j=p+1;j<q;j++)
				ans[id]+=sum[j];
		}
	}
	for (int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}
posted @ 2020-11-23 16:22  stoorz  阅读(119)  评论(0编辑  收藏  举报