题目大意:
给定长为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 ", query[i].ans); 79 } 80 81 int main() 82 { 83 input(); 84 solve(); 85 return 0; 86 }