蓝桥杯真题 - 求和 - 题解

题目链接: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=1n(x+zi)(numberx+numberzi)nxnumberx+numberxi=1nzi+xi=1nnumberzi+i=1nzinumberzi

  • 由以上公式可得:在每种颜色内部,维护三个后缀和即可边枚举 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值