2011-2012 Winter Petrozavodsk Camp, Andrew Stankevich Contest 41 (ASC 41)——莫队算法——Data Mining

/*
莫队算法是离线算法,用来解决已知l,r问你l,r里面值的问题
对块进行排序,后面的块通过前面的块左移右移得到,所以可能有的情况能得到O(1)或者较低的复杂度
排序的时候除以跟好n(?)
本题题意:从l到r区间内问有多少个不同的数,询问很多






*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#include<vector>
using namespace std;

const int MAX = 2000000 + 10;
int a[MAX];
map <int, int> table;
int res[MAX];
int ans[MAX];
int n, m;
vector<int> G[MAX];
int unit;

struct edge{
    int l, r, id;
}b[MAX];

bool cmp(edge i, edge j){
    if(i.l/unit == j.l/unit){
        return i.r/unit < j.r/unit;
    }
    return i.l/unit < j.l/unit;
}



void modui()
{
    sort(b + 1, b + m + 1, cmp);
    memset(res, 0, sizeof(res));
    int l = 1, r = 0, ret = 0;
    for(int i = 1; i <= m; i++){
        while(l < b[i].l){
            res[a[l]] --;
            if(res[a[l]] == 0)
                ret--;
            l++;
        }//l不是,那么数少一,如果该数目变成0,说明原来加了该数,总数减少1
        while(l > b[i].l){
            res[a[--l]]++;
            if(res[a[l]] == 1)
                ret++;
        }//l是,那么l-1也是(l>b[i].l)说明l少1可能就是临界值,如果变成1,说明多了一个数
        while( r < b[i].r){
            res[a[++r]]++;
            if(res[a[r]] == 1) 
                ret++;
        }
        while( r > b[i].r){
            res[a[r]] --;
            if(res[a[r]] == 0)
                ret--;
            r--;
        }
       ans[b[i].id] = ret;
       //块的编号
    }
    for(int i = 1; i <= m ; i++)
        printf("%d
",ans[i]);
}

int main()
{
	freopen ("data.in", "r", stdin);
	freopen ("data.out", "w", stdout);

    while(~scanf("%d", &n)){
        int num = 0;

        table.clear();
        for(int i = 1; i <= n ; i++)
            G[i].clear();
        for(int i = 1; i <= n ;i++){
            scanf("%d", &a[i]);
        a[i] = table[a[i]] ? table[a[i]] : table[a[i]] = ++num ;//离散化
        //如果table[a[i]] 有值,那么a[i] = table[a[i]],相当于编号,如果没有,那么向后推
        G[a[i]].push_back(i);//记录值为a[i]的各个数的位置
        }
        unit = (int)sqrt(1.0*n);
        scanf("%d", &m);
        int l, r;
        for(int i = 1; i <= m; i++){
            scanf("%d%d", &l, &r);
            r = l + r - 1;
        int pos = lower_bound(G[a[r]].begin(), G[a[r]].end(), l) - G[a[r]].begin();//二分得到r再l的右边最先出现过的位置
        r = G[a[r]][pos]  ;
        b[i].l = l; b[i].r = r; b[i].id = i;
        }
        modui();
    }
    return 0;
}
            

  

原文地址:https://www.cnblogs.com/zero-begin/p/4652484.html