莫队算法

原理

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 }
View Code

 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 }
View Code

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 }
View Code
原文地址:https://www.cnblogs.com/ivan-count/p/9407943.html