/* 莫队算法是离线算法,用来解决已知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\n",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; }
2011-2012 Winter Petrozavodsk Camp, Andrew Stankevich Contest 41 (ASC 41)——莫队算法——Data Mining
原文:http://www.cnblogs.com/zero-begin/p/4652484.html