题目
题目链接: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(msqrt{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
",ans[i]);
return 0;
}