SPOJ D-query (莫队算法)

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:

扫描二维码关注公众号,回复: 2289770 查看本文章
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;
}

感谢聪哥。

猜你喜欢

转载自blog.csdn.net/cymbals/article/details/80707097
今日推荐