【bzoj3809】Gty的二逼妹子序列

时间:2023-12-16 19:03:14

Description

Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题。
对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数。
为了方便,我们规定妹子们的美丽度全都在[1,n]中。
给定一个长度为n(1<=n<=100000)的正整数序列s(1<=si<=n),对于m(1<=m<=1000000)次询问“l,r,a,b”,每次输出sl…sr中,权值∈[a,b]的权值的种类数。

Input

第一行包括两个整数n,m(1<=n<=100000,1<=m<=1000000),表示数列s中的元素数和询问数。
第二行包括n个整数s1…sn(1<=si<=n)。
接下来m行,每行包括4个整数l,r,a,b(1<=l<=r<=n,1<=a<=b<=n),意义见题目描述。
保证涉及的所有数在C++的int内。
保证输入合法。

Output

对每个询问,单独输出一行,表示sl…sr中权值∈[a,b]的权值的种类数。

Sample Input

10 10
4 4 5 1 4 1 5 1 2 1
5 9 1 2
3 4 7 9
4 4 2 5
2 3 4 7
5 10 4 4
3 9 1 1
1 4 5 9
8 9 3 3
2 2 1 6
8 9 1 4

Sample Output

2
0
0
2
1
1
1
0
1
2

HINT

样例的部分解释:
5 9 1 2
子序列为4 1 5 1 2
在[1,2]里的权值有1,1,2,有2种,因此答案为2。
3 4 7 9
子序列为5 1
在[7,9]里的权值有5,有1种,因此答案为1。
4 4 2 5
子序列为1
没有权值在[2,5]中的,因此答案为0。
2 3 4 7
子序列为4 5
权值在[4,7]中的有4,5,因此答案为2。
建议使用输入/输出优化。
该题要求什么题目已经说得很清楚了。。。
把这个题再打一遍只不过是想在温习一下莫队算法还记不记得。。。
莫队算法果然是深入OIER的人心啊,感天动地我竟然还会打(不会打莫队真是愧对CJ前辈啊);
这个题的思想很巧妙。。。这题如果用树状数组来实现的话可以实现logn的转移和查询可以获得60分。。。
这个题的巧妙之处就是对值域进行分块!!!
这样的话莫队l和r指针移动时的转移是O(1)的,而每个询问的查询是sqrt(n)的;
查询的话还是用分快查询的常见套路,整块的直接加,不是整块的就暴力搞。。。
而出现次数的话开一个桶就可以实现了,类似HH的项链。。。
一开始l[i]成(i-1)*block了,忘了加1。。。。
附上代码:
 // MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<queue>
#include<set>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#define lson num<<1
#define rson num<<1|1
using namespace std;
typedef long long ll;
const int N=;
const int M=;
int gi()
{
int x=,flag=;
char ch=getchar();
while(ch<''||ch>''){if(ch=='-') flag=-;ch=getchar();}
while(ch>=''&&ch<='') x=x*+ch-'',ch=getchar();
return x*flag;
}
int n,m,a[N],block,pos[N],blockans[N],l[N],r[N],cnt,ans[M],tong[N];
struct ac
{
int l,r,L,R,id;
}q[M];
bool cmp(const ac &a,const ac &b)
{
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
void pre()
{
for(int i=;i<=cnt;i++) l[i]=(i-)*block+,r[i]=i*block;
r[cnt]=n;
for(int i=;i<=n;i++) pos[i]=(i-)/block+;
}
void update(int x,int val) {tong[x]+=val;}
int query(int x,int y)
{
int sum=;
if(pos[x]==pos[y])
{
for(int i=x;i<=y;i++) if(tong[i]) sum++;
}
else
{
for(int i=x;i<=r[pos[x]];i++) if(tong[i]) sum++;
for(int i=l[pos[y]];i<=y;i++) if(tong[i]) sum++;
for(int i=pos[x]+;i<=pos[y]-;i++) sum+=blockans[i];
}
return sum;
}
void work()
{
for(int i=,l=,r=;i<=m;i++)
{
while(l>q[i].l) {l--;update(a[l],);if(tong[a[l]]==) blockans[pos[a[l]]]++;}
while(r<q[i].r) {r++;update(a[r],);if(tong[a[r]]==) blockans[pos[a[r]]]++;}
while(l<q[i].l) {update(a[l],-);if(tong[a[l]]==) blockans[pos[a[l]]]--;l++;}
while(r>q[i].r) {update(a[r],-);if(tong[a[r]]==) blockans[pos[a[r]]]--;r--;}
ans[q[i].id]=query(q[i].L,q[i].R);
}
}
int main()
{
n=gi(),m=gi();
for(int i=;i<=n;i++) a[i]=gi();
for(int i=;i<=m;i++) q[i].l=gi(),q[i].r=gi(),q[i].L=gi(),q[i].R=gi(),q[i].id=i;
block=(int)sqrt(n);
if(n%block==) cnt=n/block;
else cnt=n/block+;
pre();
sort(q+,q++m,cmp);
work();
for(int i=;i<=m;i++) printf("%d\n",ans[i]);
return ;
}