Given a sequence of n numbers a1, a2, …, an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, …, aj.
Input
Line 1: n (1 ≤ n ≤ 30000).
Line 2: n numbers a1, a2, …, an (1 ≤ ai ≤ 106).
Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).
Output
For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, …, aj in a single line.
Example
Input
5
1 1 2 1 3
3
1 5
2 4
3 5
Output
3
2
3
问一个区间内有多少种数字。
曾经做过类似的区间问题,当时只有60种数字,所以可以用线段树+位操作(把数字种类用二进制记录在一个数上)来维护,但是这里有1e6种。
聪哥说这种数字特别大的一看就是莫队,所以这里用莫队!
写起来代码量意外的少,比线段树写起来舒服。
思想也很好理解,把查询排序之后就可以让每次的查询结果都基于前一个查询。
唯一不太懂的其实是为什么要分成根号n块,毕竟我没打过分块。接下来去研究一下…..
一开始用java打了这题,发现无限tle:
public class Main {
static int[] list = new int[30005];
static Query[] queries = new Query[200005];
static int[] cnt = new int[1000005];
static int[] res = new int[200005];
public static void main(String[] args) {
//Scanner reader = new Scanner(System.in);
InputReader reader = new InputReader();
PrintWriter out = new PrintWriter(System.out);
int n = reader.nextInt();
for (int i = 0; i < n; i++) {
list[i] = reader.nextInt();
}
int q = reader.nextInt();
for (int i = 0; i < q; i++) {
int l = reader.nextInt() - 1;
int r = reader.nextInt() - 1;
queries[i] = new Query(l, r, i);
}
int sqrtn = (int) Math.sqrt(n);
Arrays.sort(queries, 0, q, new Comparator<Query>() {
@Override
public int compare(Query o1, Query o2) {
int cmp = Integer.compare(o1.l / sqrtn, o2.l / sqrtn);
return cmp != 0 ? cmp : o1.r - o2.r;
}
});
int L = 1;
int R = 0;
int cur = 0;
for (int i = 0; i < q; i++) {
Query get = queries[i];
while (L < get.l) {
cur += remove(L++);
}
while (L > get.l) {
cur += add(--L);
}
while (R < get.r) {
cur += add(++R);
}
while (R > get.r) {
cur += remove(R--);
}
res[get.i] = cur;
}
for (int i = 0; i < q; i++) {
out.println(res[i]);
}
out.close();
}
public static int add(int i) {
return ++cnt[list[i]] == 1 ? 1 : 0;
}
public static int remove(int i) {
return --cnt[list[i]] == 0 ? -1 : 0;
}
static class Query {
int l, r, i;
public Query(int l, int r, int i) {
this.l = l;
this.r = r;
this.i = i;
}
}
}
输入输出挂都上了,还是不行,果然spoj慢得名不虚传……….
好吧,还是想AC,所以第一次C++就献给spoj了,以下是ac代码:
#include <bits/stdc++.h>
using namespace std;
int a[30005],cnt[1000005],res[200005];
int n,q,sqrtn;
struct Query{
int l,r,id;
}qs[200005];
bool cmp(Query q1,Query q2){
if(q1.l/sqrtn == q2.l/sqrtn){
return q1.r<q2.r;
}
return q1.l/sqrtn < q2.l/sqrtn;
}
int add(int i){
return ++cnt[a[i]] == 1?1:0;
}
int remove(int i){
return --cnt[a[i]] == 0?-1:0;
}
int main(){
scanf("%d",&n);
for(int i = 0;i<n;i++){
scanf("%d",&a[i]);
}
scanf("%d",&q);
for(int i =0;i<q;i++){
scanf("%d%d",&qs[i].l,&qs[i].r);
qs[i].l--;
qs[i].r--;
qs[i].id = i;
}
sqrtn = sqrt(n);
sort(qs,qs+q,cmp);
int L = 1;
int R = 0;
int cur = 0;
for(int i = 0;i<q;i++){
Query get = qs[i];
while(L<get.l){
cur+= remove(L++);
}
while(L>get.l){
cur+= add(--L);
}
while(R<get.r){
cur+= add(++R);
}
while(R>get.r){
cur+=remove(R--);
}
res[get.id] = cur;
}
for(int i = 0;i<q;i++){
printf("%d\n",res[i]);
}
return 0;
}
感谢聪哥。