题目链接:https://www.lanqiao.cn/problems/359/learning/
个人评价:难度 3 星(满星:5)
前置知识:乘法分配律,前缀和
整体思路
- 分析题意可知:需要将颜色相同且下标奇偶性相同的格子提出来,计算 ( x + z ) ( n u m b e r x + n u m b e r z ) ) (x+z)(number_x+number_z)) (x+z)(numberx+numberz)),如果将同种颜色但下标奇偶性不同的格子也看成不同的颜色(比如下标为奇数的格子颜色为 c o l o r i + m color_i+m colori+m),则可以先将格子按颜色分类后计算;
- 根据乘法分配律可得:
∑ i = 1 n ( x + z i ) ( n u m b e r x + n u m b e r z i ) = n x ⋅ n u m b e r x + n u m b e r x ∑ i = 1 n z i + x ∑ i = 1 n n u m b e r z i + ∑ i = 1 n z i n u m b e r z i \begin{align*} & \sum_{i=1}^n (x+z_i)(number_x+number_{z_i}) \\ = & nx\cdot number_x+number_x\sum_{i=1}^n z_i+x\sum_{i=1}^n number_{z_i}+\sum_{i=1}^n z_inumber_{z_i} \end{align*} =i=1∑n(x+zi)(numberx+numberzi)nx⋅numberx+numberxi=1∑nzi+xi=1∑nnumberzi+i=1∑nzinumberzi
- 由以上公式可得:在每种颜色内部,维护三个后缀和即可边枚举 x x x 的值边 O ( 1 ) O(1) O(1) 计算出每一个求和项的值。
过题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 200000 + 100;
const LL MOD = 10007;
struct Node {
LL idx, num;
Node() {}
Node(LL idx, LL num) : idx(idx), num(num) {}
};
int n, m;
LL ans;
int num[maxn], color[maxn];
vector<Node> G[maxn];
// 三个后缀和,分别为:下标 z_i 的后缀和,number_{z_i} 的后缀和,z_i 与 number_{z_i} 的后缀和
LL idxSum[maxn], numSum[maxn], idxNumSum[maxn];
int main() {
#ifdef ExRoc
freopen("test.txt","r",stdin);
#endif // ExRoc
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> num[i];
}
for (int i = 1; i <= n; ++i) {
cin >> color[i];
if (i % 2 == 1) {
// 通过下标奇偶性将同种颜色区分开来,奇数下标认为是一种“新颜色”
color[i] += m;
}
// 同一“新颜色”放在一个数组中处理
G[color[i]].push_back(Node(i, num[i]));
}
m *= 2;
for (int i = 1; i <= m; ++i) {
int len = G[i].size();
idxSum[len] = 0;
numSum[len] = 0;
idxNumSum[len] = 0;
// 同一“新颜色”内,预处理后缀和
for (int j = len - 1; j >= 0; --j) {
idxSum[j] = (idxSum[j + 1] + G[i][j].idx) % MOD;
numSum[j] = (numSum[j + 1] + G[i][j].num) % MOD;
idxNumSum[j] = (idxNumSum[j + 1] + G[i][j].idx * G[i][j].num % MOD) % MOD;
}
// 根据公式枚举 x,O(1) 计算答案
for (int j = 0; j < len - 1; ++j) {
ans = (ans + G[i][j].idx * G[i][j].num % MOD * (len - j - 1) % MOD) % MOD;
ans = (ans + G[i][j].idx * numSum[j + 1] % MOD) % MOD;
ans = (ans + G[i][j].num * idxSum[j + 1] % MOD) % MOD;
ans = (ans + idxNumSum[j + 1]) % MOD;
}
}
cout << ans << endl;
return 0;
}