原理
1、离线操作。
2、划分成若干块,将区间先按块排序,块内按区间右边界排序。块大小一般为sqrt(n)。
3、按照排序后的区间进行操作,不断进行区间转移,更新答案。
题目
1、小Z的袜子(hose) HYSBZ - 2038
题意:有n只袜子,求区间内颜色相同的两只袜子的概率。
思路:对于区间[l,r],其概率为sum(cnt[i]*(cnt[i-1])|i∈val[l,..,r])/[(r-l+1)*(r-l)]。按照莫队的思想更新分子即可。

1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 const int maxn = 5e5 + 10; 7 const int maxm = 5e5 + 10; 8 int n, m; 9 int unit_len; 10 int be[maxn];//记录下标L属于哪一个分块 11 int val[maxn]; 12 int cnt[maxn]; 13 long long ans; 14 long long ansa[maxn],ansb[maxn]; //答案a / b 15 struct node 16 { 17 int l, r,id;//区间[l,r]、输入顺序 18 friend bool operator<(const node&n1, const node&n2) 19 { 20 if (be[n1.l] == be[n2.l]) return n1.r < n2.r; 21 else return n1.l < n2.l; 22 } 23 }qs[maxm]; 24 long long GCD(long long a, long long b) 25 { 26 if (a < b) swap(a, b); 27 while (b) 28 { 29 long long tmp = a % b; 30 a = b; 31 b = tmp; 32 } 33 return a; 34 } 35 long long cal(int x) 36 { 37 return 1ll * x*(x - 1); 38 } 39 void update(int pos, int v) 40 { 41 ans -= cal(cnt[val[pos]]); 42 cnt[val[pos]] += v; 43 ans += cal(cnt[val[pos]]); 44 } 45 void solve() 46 { 47 int l = 1, r = 0; 48 ans = 0; 49 for (int i = 1; i <= m; i++) 50 { 51 int id = qs[i].id; 52 while (r < qs[i].r) update(r + 1, 1), r++; 53 while (r > qs[i].r) update(r, -1),r--; 54 while (l < qs[i].l) update(l, -1), l++; 55 while (l > qs[i].l) update(l - 1, 1), l--; 56 if (ans == 0) ansa[id] = 0, ansb[id] = 1; 57 else 58 { 59 ansb[id]= cal(qs[i].r - qs[i].l + 1); 60 ansa[id]= ans; 61 long long gcd = GCD(ansa[id],ansb[id]); 62 ansa[id]/= gcd; 63 ansb[id] /= gcd; 64 } 65 } 66 } 67 int main() 68 { 69 while (~scanf("%d%d", &n, &m)) 70 { 71 unit_len = sqrt(n); 72 for (int i = 1; i <= n; i++) scanf("%d", &val[i]); 73 for (int i = 1; i <= n; i++) be[i] = i / unit_len + 1; 74 for (int i = 1; i <= m; i++) scanf("%d%d", &qs[i].l, &qs[i].r),qs[i].id=i; 75 sort(qs + 1, qs + 1 + m); 76 solve(); 77 for (int i = 1; i <= m; i++) 78 { 79 printf("%lld/%lld ", ansa[i], ansb[i]); 80 } 81 } 82 83 84 return 0; 85 }
2、Sona NBUT - 1457
题意:求区间内,每种数值的三次方的和。
思路:因为数值很大,需要离散化,接下来套莫队算法即可。注意输出long long用%64d。

1 #include<iostream> 2 #include<algorithm> 3 #include<vector> 4 #include<cstdio> 5 #include<cmath> 6 using namespace std; 7 const int maxn = 1e5 + 10; 8 int val[maxn]; 9 int nid[maxn]; 10 int be[maxn]; 11 int unit_len; 12 int n, q,sz; 13 vector<int>all; 14 int cnt[maxn]; 15 long long tans; 16 long long cal_cube(int x) 17 { 18 return 1ll * x*x*x; 19 } 20 struct op 21 { 22 int l, r,id; 23 friend bool operator<(const op&o1, const op&o2) 24 { 25 if (be[o1.l] == be[o2.l]) return o1.r < o2.r; 26 else return o1.l < o2.l; 27 } 28 }qs[maxn]; 29 long long ans[maxn]; 30 void update(int pos, int v) 31 { 32 tans -= cal_cube(cnt[pos]); 33 cnt[pos] += v; 34 tans += cal_cube(cnt[pos]); 35 } 36 void solve() 37 { 38 tans = 0; 39 int l = 1, r = 0; 40 for (int i = 1; i <= q; i++) 41 { 42 while (r < qs[i].r) update(nid[r + 1], 1), r++; 43 while (l > qs[i].l) update(nid[l - 1], 1), l--; 44 45 while (r > qs[i].r) update(nid[r], -1), r--; 46 while (l < qs[i].l) update(nid[l], -1), l++; 47 ans[qs[i].id] = tans; 48 } 49 } 50 int main() 51 { 52 while (~scanf("%d", &n)) 53 { 54 all.clear(); 55 for (int i = 1; i <= n; i++) scanf("%d", &val[i]),all.push_back(val[i]); 56 sort(all.begin(), all.end()); 57 all.erase(unique(all.begin(), all.end()), all.end()); 58 sz = all.size(); 59 for (int i = 1; i <= n; i++) nid[i] = lower_bound(all.begin(), all.end(), val[i]) - all.begin() + 1; 60 unit_len = (int)sqrt(1.0*n); 61 for (int i = 1; i <= n; i++) be[i] = i / unit_len + 1,cnt[i]=0; 62 scanf("%d", &q); 63 for (int i = 1; i <= q; i++) 64 { 65 scanf("%d%d", &qs[i].l, &qs[i].r); 66 qs[i].id = i; 67 } 68 sort(qs + 1, qs + 1 + q); 69 solve(); 70 for (int i = 1; i <= q; i++) printf("%lld ", ans[i]); 71 } 72 73 74 return 0; 75 }
3、Little Elephant and Array CodeForces-220B
题意:给出一个数组,每次询问一个区间内出现次数恰好等于该数自身这样的数的个数。
思路:莫队算法。注意,值大于1e5的都可以不用考虑,因为区间最大为1e5。

1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 const int maxn = 1e5 + 10; 7 const int maxm = 1e5 + 10; 8 int n, m; 9 int unit_len; 10 int be[maxn]; //记录下标L属于哪一个分块 11 int val[maxn]; 12 int cnt[maxn]; 13 long long ans; 14 long long ansq[maxn]; //答案 15 struct node 16 { 17 int l, r, id; //区间[l,r]、输入顺序 18 friend bool operator<(const node&n1, const node&n2) 19 { 20 if (be[n1.l] == be[n2.l]) 21 return n1.r < n2.r; 22 else 23 return n1.l < n2.l; 24 } 25 } qs[maxm]; 26 27 long long cal(int cnt_x, int x) 28 { 29 if (cnt_x == x) 30 return 1; 31 else 32 return 0; 33 } 34 void update(int pos, int v) 35 { 36 if (val[pos] >= maxn) 37 return; 38 ans -= cal(cnt[val[pos]], val[pos]); 39 cnt[val[pos]] += v; 40 ans += cal(cnt[val[pos]], val[pos]); 41 } 42 void solve() 43 { 44 int l = 1, r = 0; 45 ans = 0; 46 for (int i = 1; i <= m; i++) 47 { 48 int id = qs[i].id; 49 while (r < qs[i].r) 50 update(r + 1, 1), r++; 51 while (r > qs[i].r) 52 update(r, -1), r--; 53 while (l < qs[i].l) 54 update(l, -1), l++; 55 while (l > qs[i].l) 56 update(l - 1, 1), l--; 57 ansq[id] = ans; 58 } 59 } 60 int main() 61 { 62 scanf("%d%d", &n, &m); 63 unit_len = sqrt(n); 64 for (int i = 1; i <= n; i++) 65 scanf("%d", &val[i]); 66 for (int i = 1; i <= n; i++) 67 be[i] = i / unit_len + 1; 68 for (int i = 1; i <= m; i++) 69 scanf("%d%d", &qs[i].l, &qs[i].r), qs[i].id = i; 70 sort(qs + 1, qs + 1 + m); 71 solve(); 72 for (int i = 1; i <= m; i++) 73 { 74 printf("%lld ", ansq[i]); 75 } 76 return 0; 77 }