Codeforces 86D - Powerful array - 莫队

题目大意:

给定长为N的序列a[1..N]和T次询问,每次询问指定一个子段a[L..R],设某个数v在该子段中出现k(v)次,那么该次询问的结果为Σv*k(v)*k(v)。

N,T≤2e5,1≤a[i]≤1e6。

莫队算法初次听起来可能感觉很神奇,不过原理和实现方法都很简单。这里有篇很不错的英文讲解:https://www.hackerearth.com/zh/practice/notes/mos-algorithm/

应用莫队算法的前提:(1)静态序列,无修改操作;(2)离线询问。

莫队算法的流程:(直接从上边的网址Ctrl-C过来的)

Step 1:
    Denote BLOCK_SIZE = sqrt(N);
Step 2:
    Rearrange all queries in a way we will call “Mo’s order”. It is defined like this: [L1, R1] comes earlier than [L2, R2] in Mo’s order if and only if:
   a) L1 / BLOCK_SIZE < L2 / BLOCK_SIZE
   b) L1 / BLOCK_SIZE == L2 / BLOCK_SIZE && R1 < R2
Step 3:
    Maintain segment [mo_left, mo_right] for which we know Func([mo_left, mo_right]). Initially, this segment is empty. We set mo_left = 0 and mo_right = -1;(注:原文约定序列下标从0开始)
Step 4:
    Answer all queries following Mo’s order. Suppose the next query you want to answer is [L, R]. Then you perform these steps:
   a) while mo_right is less than R, extend current segment to [mo_left, mo_right + 1];
   b) while mo_right is greater than R, cut current segment to [mo_left, mo_right - 1];
   c) while mo_left is greater than L, extend current segment to [mo_left - 1, mo_right];
   d) while mo_left is less than L, cut current segment to [mo_left + 1, mo_right].

本题的代码如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 using LL = long long;
 6 
 7 const int blockSize = 450;
 8 const int maxN = (int)2e5 + 10;
 9 
10 int A[maxN], N, T;
11 int C[(int)1e6 + 1];
12 LL curAns;
13 
14 struct Query
15 {
16     int id;
17     int left, right;
18     int blockId;
19     LL ans;
20 
21     bool operator < (const Query& rhs) const {
22         return blockId < rhs.blockId || (blockId == rhs.blockId && right < rhs.right);
23     }
24 };
25 Query query[maxN];
26 
27 void input()
28 {
29     scanf("%d%d", &N, &T);
30     for (int i = 1; i <= N; i++)
31         scanf("%d", A + i);
32     for (int l, r, i = 1; i <= T; i++)
33     {
34         scanf("%d%d", &l, &r);
35         query[i] = {i, l, r, l / blockSize, 0LL};
36     }
37     std::sort(query + 1, query + T + 1);
38 }
39 
40 inline void add(int pos)
41 {
42     int &c = C[A[pos]];
43     curAns += (2LL * c + 1) * A[pos];
44     c += 1;
45 }
46 inline void subtract(int pos)
47 {
48     int &c = C[A[pos]];
49     curAns -= (2LL * c - 1) * A[pos];
50     c -= 1;
51 }
52 
53 void process()
54 {
55     int left = 0, right = 0;
56     for (int i = 1; i <= T; i++)
57     {
58         while (left < query[i].left)
59             subtract(left++);
60         while (left > query[i].left)
61             add(--left);
62         while (right < query[i].right)
63             add(++right);
64         while (right > query[i].right)
65             subtract(right--);
66 
67         query[i].ans = curAns;
68     }
69 }
70 
71 void solve()
72 {
73     process();
74     std::sort(query + 1, query + T + 1, [] (const Query& A, const Query& B) {
75         return A.id < B.id;
76     });
77     for (int i = 1; i <= T; i++)
78         printf("%lld\n", query[i].ans);
79 }
80 
81 int main()
82 {
83     input();
84     solve();
85     return 0;
86 }

 

转载于:https://www.cnblogs.com/Onlynagesha/p/8797262.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值