DQUERY - D-query
题目描述
Given a sequence of (n) numbers (a_{1},a_{2},...,a_{n}) and a number of d-queries. A d-query is a pair ((i,j)) ((1 le i le j le n)). For each d-query ((i,j)), you have to return the number of distinct elements in the subsequence (a_{i},a _{i+1},...,a_{j}).
输入格式
- Line 1: (n) ((1 le n le 30000)).
- Line 2: n numbers (a_{1},a_{2},...,a_{n}) ((1 le a_{i} le 10^{6})).
- Line 3: (q) ((1 le q le 200000)), the number of d-queries.
- In the next (q) lines, each line contains (2) numbers (i),(j) representing a d-query ((1 le i le j le n)).
输出格式
- For each d-query ((i,j)), print the number of distinct elements in the subsequence (a_{i},a_{i+1},...,a_{j}) in a single line.
题意翻译
给出一个长度为(n)的数列,(a_{1},a_{2},...,a_{n}),有(q)个询问,每个询问给出数对((i,j)),需要你给出(a_{i},a_{i+1},...,a_{j})这一段中有多少不同的数字
样例输入
5 1 1 2 1 3 3 1 5 2 4 3 5
样例输出
3 2 3
题解
题意:看翻译吧。。。
这是一道莫队板题。(明显的)
本人其实也是刚学莫队,对于莫队可能也有一些误解,如果有不正确的解释请各位大佬指出。
因为做到一个据说需要莫队的题目,所以开始学习莫队了。
原本以为莫队是很难的,看了别人的博客后感觉看不懂(优雅的暴力?),然后开始看别人写的代码和板题。
然后。。。
这真TM是优雅的暴力!!!
好了,吐槽结束,其实大家只要知道莫队其实本质是暴力,只不过用特殊的方法排了个序而已。
我们考虑这样一种暴力,用(cnt[])和(l,r)维护(l)到(r)区间内各个数字的个数(因为数字的大小最大为(10^6)),并且维护一个(ans)值,然后我们移动(l)和(r)的时候可以快速地维护这个数组和(ans)值了。
但是我们可以发现直接这样维护的话时间复杂度还是(O(n^2))的。
那么我们考虑离线操作,对输入的询问排个序。
如果我们按照询问的(l)值从小到大排序,使维护的区间的(l)只会往右移动,那么可能可以减少一点时间,但是想一下就会发现这种方法是可能被卡成(O(n^2))的,比如询问按照(l)排序后(r)的值是一大一小,那么每次询问的时间复杂度都是(O(n))。
那么我们开始讲莫队,莫队就是利用了分块的思想,我们把询问中(frac {l}{sqrt{n}})相同的数分为一组(这里分块的大小是(sqrt{n}),一般来说随机数据的话区(sqrt{n})会比较好,但是不同的题目分块的大小也可以不同)
然后在同一组内的询问,我们按照询问的(r)值从小到大排序。
这样你就会发现在每(sqrt{n})次询问内,因为每个区间(r)值是从小到大排序的,所以你区间移动的距离最多为(n+sqrt{n}*sqrt{n})(近似为(n)),那么莫队的时间复杂度就是(O(nsqrt{n}))。
上代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[30009];
struct aa{
int l,r,x,ans;
}p[200009];
int sq;
bool cmp(aa x,aa y){//按照莫队算法排序
if(x.l/sq<y.l/sq) return 1;
if(x.l/sq>y.l/sq) return 0;
return x.r<y.r;
}
int cnt[1000009];
bool cmpp(aa x,aa y){return x.x<y.x;}
int main(){
scanf("%d",&n);
sq=sqrt(n);
for(int j=1;j<=n;j++)
scanf("%d",&a[j]);
scanf("%d",&m);
for(int j=1;j<=m;j++){
scanf("%d%d",&p[j].l,&p[j].r);
p[j].x=j;//因为最后输出要按输入的顺序输出,所以这里记录输入的顺序
}
sort(p+1,p+m+1,cmp);
int ll=1,rr=1,ss=1;
cnt[a[1]]=1;
for(int j=1;j<=m;j++){
while(rr<p[j].r){
rr++;
cnt[a[rr]]++;
if(cnt[a[rr]]==1) ss++;
}
while(ll>p[j].l){
ll--;
cnt[a[ll]]++;
if(cnt[a[ll]]==1) ss++;
}
while(rr>p[j].r){
cnt[a[rr]]--;
if(cnt[a[rr]]==0) ss--;
rr--;
}
while(ll<p[j].l){
cnt[a[ll]]--;
if(cnt[a[ll]]==0) ss--;
ll++;
}
p[j].ans=ss;//记录答案
}
sort(p+1,p+m+1,cmpp);
for(int j=1;j<=m;j++)
printf("%d
",p[j].ans);
return 0;
}