(莫队算法)两题莫队算法统计数量的入门题

因为这两题差不多,而且比较简单,就放一起,做了这题,这种题目就是巨水的题了。随便写都行。

 

CodeForces - 86D  Powerful array

 

题意:

多次查询数列中从L到R每个数字出现次数的平方乘这个数字的和。

 

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <cmath>
 5 #include <map>
 6 #include <vector>
 7 #include <algorithm>
 8 
 9 using namespace std;
10 
11 const int maxn = 200010;
12 const int bk = 350;
13 int n, q;
14 int a[maxn];
15 struct Query {
16     int l, r, index;
17 };
18 int cnt[maxn * 5];
19 
20 Query query[maxn];
21 
22 bool cmp(Query a, Query b) {
23     if(a.l / bk == b.l / bk)return a.r < b.r;
24     else return a.l / bk < b.l / bk;
25 }
26 
27 long long ans[maxn];
28 long long res = 0;
29 
30 void add(int x) {
31     res += x * (2 * cnt[x] + 1);
32     cnt[x]++;
33 }
34 
35 void del(int x) {
36     cnt[x]--;
37     res -= x * (2 * cnt[x] + 1);
38 }
39 
40 int main() {
41     scanf("%d", &n);
42     scanf("%d", &q);
43     for(int i = 1; i <= n; i++) {
44         scanf("%d", &a[i]);
45     }
46     for(int i = 0; i < q; i++) {
47         scanf("%d%d", &query[i].l, &query[i].r);
48         query[i].index = i;
49     }
50     sort(query, query + q, cmp);
51     int left = 1, right = 0;
52     for(int i = 0; i < q; i++) {
53         if(right < query[i].r) {
54             for(int j = right + 1; j <= query[i].r; j++) {
55                 add(a[j]);
56             }
57         } else {
58             for(int j = right; j > query[i].r; j--) {
59                 del(a[j]);
60             }
61         }
62         right = query[i].r;
63         if(left < query[i].l) {
64             for(int j = left; j < query[i].l; j++) {
65                 del(a[j]);
66             }
67 
68         } else {
69             for(int j = left - 1; j >= query[i].l; j--) {
70                 add(a[j]);
71             }
72         }
73         left = query[i].l;
74         ans[query[i].index] = res;
75     }
76     for(int i = 0; i < q; i++) {
77         printf("%lld\n", ans[i]);
78 
79     }
80 
81     return 0;
82 }
View Code

 

NBUT - 1457 Sona

 

题意:

多次查询数列中L到R买个数字出现次数的立方和。

 

特别注意:

注意复杂度,因为数值较大,如果用map加个log的复杂度,可能会超时,而这个OJ没有开C++11,无法使用unordered_map,所以一定要先离散化。

毕竟因为答案只跟次数有关跟值无关,所以无影响。

 

代码:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <cmath>
  5 #include <map>
  6 #include <vector>
  7 #include <algorithm>
  8 
  9 using namespace std;
 10 
 11 inline bool scan_d(int &num) {
 12     char in;
 13     bool IsN = false;
 14     in = getchar();
 15     if(in == EOF) return false;
 16     while(in != '-' && (in < '0' || in > '9')) in = getchar();
 17     if(in == '-') {
 18         IsN = true;
 19         num = 0;
 20     } else num = in - '0';
 21     while(in = getchar(), in >= '0' && in <= '9') {
 22         num *= 10, num += in - '0';
 23     }
 24     if(IsN) num = -num;
 25     return true;
 26 }
 27 
 28 
 29 const int maxn = 100010;
 30 const int bk = 400;
 31 int n, q;
 32 int a[maxn];
 33 int b[maxn];
 34 struct Query {
 35     int l, r, index;
 36 };
 37 int cnt[maxn];
 38 
 39 Query query[maxn];
 40 
 41 bool cmp(Query a, Query b) {
 42     if(a.l / bk == b.l / bk)return a.r < b.r;
 43     else return a.l / bk < b.l / bk;
 44 }
 45 
 46 long long ans[maxn];
 47 long long res = 0;
 48 
 49 void add(int x) {
 50     res += 3 * cnt[x] * cnt[x] + 3 * cnt[x] + 1;
 51     cnt[x]++;
 52 }
 53 
 54 void del(int x) {
 55     cnt[x]--;
 56     res -= 3 * cnt[x] * cnt[x] + 3 * cnt[x] + 1;
 57 }
 58 
 59 
 60 void init() {
 61     sort(b + 1, b + n + 1);
 62     int pp = unique(b + 1, b + n + 1) - b;
 63     for(int i = 1; i <= n; i++) {
 64         a[i] = lower_bound(b + 1, b + pp + 1, a[i]) - b;
 65     }
 66 }
 67 
 68 int main() {
 69     while(scan_d(n)) {
 70         memset(cnt, 0, sizeof cnt);
 71         res = 0;
 72         for(int i = 1; i <= n; i++) {
 73             scan_d(a[i]);
 74             b[i] = a[i];
 75         }
 76         init();
 77         scan_d(q);
 78         for(int i = 0; i < q; i++) {
 79             scan_d(query[i].l);
 80             scan_d(query[i].r);
 81             query[i].index = i;
 82         }
 83         sort(query, query + q, cmp);
 84         int left = 1, right = 0;
 85         for(int i = 0; i < q; i++) {
 86             if(right < query[i].r) {
 87                 for(int j = right + 1; j <= query[i].r; j++) {
 88                     add(a[j]);
 89                 }
 90             } else {
 91                 for(int j = right; j > query[i].r; j--) {
 92                     del(a[j]);
 93                 }
 94             }
 95             right = query[i].r;
 96             if(left < query[i].l) {
 97                 for(int j = left; j < query[i].l; j++) {
 98                     del(a[j]);
 99                 }
100 
101             } else {
102                 for(int j = left - 1; j >= query[i].l; j--) {
103                     add(a[j]);
104                 }
105             }
106             left = query[i].l;
107             ans[query[i].index] = res;
108         }
109         for(int i = 0; i < q; i++) {
110             printf("%I64d\n", ans[i]);
111         }
112     }
113 
114     return 0;
115 }
View Code

 

转载于:https://www.cnblogs.com/tak-fate/p/7143656.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值