# 莫队算法——解决序列上询问的利器

#include <bits/stdc++.h>
using namespace std ;
const int maxn = 50010 ;
int n, m, a[maxn] ;
bool vis[maxn] ;
int main() {
int i, j, k, query_time, L, R ;
cin >> n >> query_time >> k ;
for ( i = 1 ; i <= n ; i ++ )
cin >> a[i] ;
while ( query_time -- ) {
cin >> L >> R ;
memset ( vis, 0, sizeof(vis) ) ;
for ( i = L ; i <= R ; i ++ )
vis[a[i]] = true ;
int ans = 0 ;
for ( i = 0 ; i <= k ; i ++ )
ans += vis[i] ;
cout << ans << endl ;
}
return 0 ;
}

void add ( int pos ) {
++cnt[a[pos]] ;
if ( cnt[a[pos]] == 1 )
}
void remove ( int pos ) {
-- cnt[a[pos]] ;
if ( cnt[a[pos]] == 0 )
}
void solve() {
int curL = 1, curR = 0 ; // current L R
for ( each query [L,R] ) {
while ( curL < L )
remove ( curL++ ) ;
while ( curL > L )
while ( curR < R )
while ( curR > R )
remove ( curR-- ) ;
cout << answer << endl ;
// Warning : please notice the order "--","++" and "cur" ;
}
}

手玩数据：
6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6

3
2
2

{0, 3} {1, 7} {2, 8} {7, 8} {4, 8} {4, 4} {1, 2}

{0, 3} {1, 7} {2, 8} {1, 2} | {4, 8} {4, 4} | {7, 8}

{1, 2} {0, 3} {1, 7} {2, 8} | {4, 4} {4, 8} | {7, 8}

O(N)$O(\sqrt{N})$的。所以，左指针的移动总量是O(MN)$O(M*\sqrt{N})$

### 例题1：BZOJ3781 小B的询问

#### Output

M行，每行一个整数，其中第i行的整数表示第i个询问的答案。

6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6

6
9
5
2

#### HINT

/**************************************************************
Source Code : GoAway
Date : 2017-02-06
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#include <set>
#include <cmath>
#include <algorithm>
#include <ctime>

using namespace std ;
const int zhf = 1<<30 ;
const int maxn = 50010, tim = 250 ;
bool Read ( int &x ) {
bool f = 0 ; x = 0 ; char c = getchar() ;
while ( !isdigit(c) ) {
if ( c == '-' ) f = 1 ;
if ( c == EOF ) return false ;
c = getchar() ;
}
while ( isdigit(c) ) {
x = 10 * x + c - '0' ;
c = getchar() ;
}
if ( f ) x = -x ;
return true ;
}

struct query {
int L, R, id ;
friend bool operator < ( query a, query b ) {
return (a.L/tim) == (b.L/tim) ? a.R < b.R : a.L < b.L ;
}
} e[maxn] ;

int n, m, a[maxn], cnt[maxn], ans[maxn], answer ;
void add ( int pos ) {
}
void remove ( int pos ) {
}

int main() {
int i, j, k, curL = 1, curR = 0 ;
for ( i = 1 ; i <= n ; i ++ )
for ( i = 1 ; i <= m ; i ++ ) {
e[i].id = i ;
}
sort ( e+1, e+m+1 ) ;
for ( i = 1 ; i <= m ; i ++ ) {
int L = e[i].L, R = e[i].R ;
while ( curL < L )
remove ( curL++ ) ;
while ( curL > L )
while ( curR < R )
while ( curR > R )
remove ( curR-- ) ;
}
for ( i = 1 ; i <= m ; i ++ )
printf ( "%d\n", ans[i] ) ;
return 0 ;
}

### 例题2：SDOI2009 HH的项链 洛谷1972

#### Description

HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运，所以每次散步 完后，他都会随意取出一段贝壳，思考它们所表达的含义。HH不断地收集新的贝壳，因此， 他的项链变得越来越长。有一天，他突然提出了一个问题：某一段贝壳中，包含了多少种不同 的贝壳？这个问题很难回答。。。因为项链实在是太长了。于是，他只好求助睿智的你，来解 决这个问题。

#### Output

M行，每行一个整数，依次表示询问对应的答案。

6
1 2 3 4 3 5
3
1 2
3 5
2 6

2
2
4

#### HINT

/**************************************************************
Source Code : GoAway
Date : 2017-02-06
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#include <set>
#include <cmath>
#include <algorithm>
#include <ctime>

using namespace std ;
const int zhf = 1<<30 ;
const int maxn = 1000010 ;
bool Read ( int &x ) {
bool f = 0 ; x = 0 ; char c = getchar() ;
while ( !isdigit(c) ) {
if ( c == '-' ) f = 1 ;
if ( c == EOF ) return false ;
c = getchar() ;
}
while ( isdigit(c) ) {
x = 10 * x + c - '0' ;
c = getchar() ;
}
if ( f ) x = -x ;
return true ;
}

int n, m, cnt[maxn], a[maxn], answer, ans[maxn], tim ;
struct query {
int l, r, id ;
friend bool operator < ( query a, query b ) {
return (a.l/tim) == (b.l/tim) ? a.r < b.r : a.l<b.l ;
}
} e[maxn] ;
void add ( int pos ) {
if ( (++cnt[a[pos]]) == 1 ) ++ answer ;
}
void remove ( int pos ) {
if ( (--cnt[a[pos]]) == 0 ) -- answer ;
}
int main() {
int i, j, k, curL = 1, curR = 0 ;
for ( i = 1 ; i <= n ; i ++ )
Read(m) ; tim = sqrt(m) ;
for ( i = 1 ; i <= m ; i ++ ) {
e[i].id = i ;
}
sort(e+1,e+m+1) ;
for ( i = 1 ; i <= m ; i ++ ) {
int L = e[i].l, R = e[i].r ;
while ( curL < L )
remove(curL++) ;
while ( curL > L )
while ( curR < R )
while ( curR > R )
remove(curR--) ;
}
for ( i = 1 ; i <= m ; i ++ )
printf ( "%d\n", ans[i] ) ;
return 0 ;
}

### 例题3：2009国家集训队 小Z的袜子 清橙OJ1206

作为一个生活散漫的人，小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天，小Z再也无法忍受这恼人的找袜子过程，于是他决定听天由命……
具体来说，小Z把这N只袜子从1到N编号，然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双，甚至不在意两只袜子是否一左一右，他却很在意袜子的颜色，毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z，他有多大的概率抽到两只颜色相同的袜子。当然，小Z希望这个概率尽量高，所以他可能会询问多个(L,R)以方便自己选择。

输入文件第一行包含两个正整数N和M。N为袜子的数量，M为小Z所提的询问的数量。
接下来一行包含N个正整数Ci，其中Ci表示第i只袜子的颜色，相同的颜色用相同的数字表示。
再接下来M行，每行两个正整数L，R表示一个询问。

输出文件包含M行，对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1，否则输出的A/B必须为最简分数。（详见样例）

6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6

2/5
0/1
1/1
4/15

询问1：共C(5,2)=10种可能，其中抽出两个2有1种可能，抽出两个3有3种可能，概率为(1+3)/10=4/10=2/5。
询问2：共C(3,2)=3种可能，无法抽到颜色相同的袜子，概率为0/3=0/1。
询问3：共C(3,2)=3种可能，均为抽出两个3，概率为3/3=1/1。
注：上述C(a, b)表示组合数，组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。

30%的数据中 N,M ≤ 5000；
60%的数据中 N,M ≤ 25000；
100%的数据中 N,M ≤ 50000，1 ≤ L < R ≤ N，Ci ≤ N。

（有没有感觉自己被续了一秒

/**************************************************************
Source Code : GoAway
Date : 2017-02-06
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#include <set>
#include <cmath>
#include <algorithm>
#include <ctime>
#define ll long long
using namespace std ;
const ll zhf = 1<<30 ;
const ll maxn = 50010 ;
bool Read ( ll &x ) {
bool f = 0 ; x = 0 ; char c = getchar() ;
while ( !isdigit(c) ) {
if ( c == '-' ) f = 1 ;
if ( c == EOF ) return false ;
c = getchar() ;
}
while ( isdigit(c) ) {
x = 10 * x + c - '0' ;
c = getchar() ;
}
if ( f ) x = -x ;
return true ;
}
ll tim ;
struct query {
ll L, R, id ;
friend bool operator < ( query a, query b ) {
return (a.L/tim) == (b.L/tim) ? a.R < b.R : a.L < b.L ;
}
} e[maxn] ;
ll gcd ( ll x, ll y ) {
return y ? gcd ( y, x%y ) : x ;
}
ll x, y ;
void out() {
if ( !x ) puts("0/1") ;
else {
ll d = gcd(x, y) ;
x /= d ; y /= d ;
printf ( "%lld/%lld\n", x, y ) ;
}
}
} ans[maxn] ;

ll n, m, a[maxn], cnt[maxn], answer ;
void add ( ll pos ) {
++ cnt[a[pos]] ;
if ( cnt[a[pos]] > 1 )
answer += cnt[a[pos]] - 1 ;
}
void remove ( ll pos ) {
-- cnt[a[pos]] ;
if ( cnt[a[pos]] > 0 ) answer -= cnt[a[pos]] ;
}
ll sum ( ll x ) { return x*(x+1)/2 ; }
int main() {
ll i, j, k, curL = 1, curR = 0 ;
tim = sqrt(m) ;
for ( i = 1 ; i <= n ; i ++ )
for ( i = 1 ; i <= m ; i ++ ) {
e[i].id = i ;
}
sort ( e+1, e+m+1 ) ;
for ( i = 1 ; i <= m ; i ++ ) {
ll L = e[i].L, R = e[i].R ;
while ( curL < L )
remove ( curL++ ) ;
while ( curL > L )
while ( curR < R )
while ( curR > R )
remove ( curR-- ) ;
}