题目:
可执行代码:
cpp
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;//数组长度
const int p=10007;//取模
long long num[N],col[N];
long long inum[N][2],Num[N][2],I[N][2],cnt[N][2];//【二维中的列[2]表示的都是区分奇数[i%2==1]还是偶数[i%2==0]】第一个表示当前的元素下标(x)*该下标(x)对应的元素的num[x]值;
//第二个表示所有的num[i]之和【区分奇偶】;第三个表示所有的i之和【区分奇偶】;第四个表示同颜色、同奇偶的元素数量 + 1 ,避免第23行代码多算了次数【区分奇偶】
int main(){
long long n=0,m=0;cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>num[i];
num[i]%=p;
}
for(int i=1;i<=n;i++) cin>>col[i];
long long ans=0;//最终答案
for(int i=1;i<=n;i++){
int x=i%2;
int c=col[i];
//导入当前数据【第一次导入的都是0】
ans=(ans+inum[c][x])%p;
ans=(ans+(i*Num[c][x]%p)%p)%p;
ans=(ans+(num[i]*I[c][x]%p)%p)%p;
ans=(ans+(i*num[i]%p*cnt[c][x])%p)%p;
//更新数据,方便下一次导入
inum[c][x]=(inum[c][x]+(i*num[i])%p)%p;
Num[c][x]=(Num[c][x]+num[i])%p;
I[c][x]=(I[c][x]+i)%p;
cnt[c][x]+=1;
}
cout<<ans;
return 0;
}
思路及代码解析:
这道题的完整讲解:同色同奇偶与前缀和优化
这道题最清晰、最详细、最容易听懂的完整讲解
我保证你看完彻底懂,以后遇到同类题直接秒写!
一、先搞懂题目到底要什么
题目:
给你一排 n 个位置
每个位置有:
- 数字 num[i]
- 颜色 col[i]
要求找所有三元组 (x, y, z) 满足:
- x < y < z
- y - x = z - y → 也就是 x 和 z 奇偶相同
- col[x] == col[z]
- 分数 = (x + z) × (num[x] + num[z])
- 把所有满足条件的分数加起来,模 10007 输出
二、为什么暴力会超时?
暴力要两层循环:
|-------------------------------------------------------------------------|
| C++ for(x=1;x<=n;x++) for(z=x+2;z<=n;z++) if(同奇偶 && 同颜色) ans += ... |
n=1e5 → 100亿次循环 → 直接超时
所以必须用 O(n) 前缀和优化
三、核心数学公式(必须看懂)
题目要求:
|---------------------------------------------|
| Plain Text (x + z) * (num[x] + num[z]) |
展开后:
|-----------------------------------------------------|
| Plain Text (x+z)*(a+b) = x*a + x*b + z*a + z*b |
(a=num[x], b=num[z])
这四项,我们可以用前缀和一次性算完!
四、代码里四个数组的作用(最关键)
|-----------------------------------------------------------------------------------------------------------------|
| C++ sum[c][x] = 累计 i * num[i] isum[c][x] = 累计 num[i] pos[c][x] = 累计 下标 i cnt[c][x] = 累计 个数 |
其中:
- c = 颜色
- x = 下标奇偶(0偶 / 1奇)
一句话:
按【颜色+奇偶】分组,每组存4个前缀和
五、代码逐行详细运行过程
1. 输入部分
|------------------------------------------------------------------------------------------------------------------|
| C++ cin >> n >> m; for (int i=1; i<=n; i++) cin >> num[i]; for (int i=1; i<=n; i++) cin >> col[i]; |
读入 n、m、每个位置的数字、颜色。
2. 遍历每个位置 i(当作 z)
|---------------------------------|
| C++ for (int i=1; i<=n; i++) { |
我们把当前遍历到的 i 当作 z
前面所有满足条件的 x 都已经被记录在前缀和里了!
3. 获取当前颜色和奇偶
|-------------------------------------------------------------|
| C++ int x = i % 2; // 0=偶数位,1=奇数位 int c = col[i]; // 当前颜色 |
只有 同颜色 + 同奇偶 的 x 才能和当前 i 配对!
4. 直接用前缀和算四项贡献(O(1))
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C++ // 第1项:x*a ans += sum[c][x]; // 第2项:x*b ans += i * isum[c][x]; // 第3项:z*a ans += num[i] * pos[c][x]; // 第4项:z*b ans += i * num[i] * cnt[c][x]; |
这四行 = 暴力两层循环的所有结果!
5. 把当前 i 加入前缀和(给后面用)
|---------------------------------------------------------------------------------------------------------|
| C++ sum[c][x] += i * num[i]; isum[c][x] += num[i]; pos[c][x] += i; cnt[c][x] += 1; |
相当于把当前 i 存起来,让后面的 z 可以直接用它算贡献。
六、最直观的总结(背这个就够)
整个代码只做两件事:
- 算当前 i 与前面所有合法 x 的贡献
- 把当前 i 存进前缀和
时间复杂度:
O(n) ------ 1e5 数据秒过
七、你给的代码 = 完美标准AC代码
你现在这段代码:
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C++ #include <bits/stdc++.h> using namespace std; #define int long long const int N = 1e5 + 9; const int p = 10007; int num[N], col[N]; int sum[N][2], isum[N][2], pos[N][2], cnt[N][2]; signed main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int n, m;cin >> n >> m; for (int i = 1; i <= n; i++)cin >> num[i], num[i] %= p; for (int i = 1; i <= n; i++)cin >> col[i]; int ans = 0; for (int i = 1; i <= n; i++) { int x = i % 2; int c = col[i]; ans = (ans + sum[c][x]) % p; ans = (ans + (i * isum[c][x]) % p) % p; ans = (ans + (num[i] * pos[c][x]) % p) % p; ans = (ans + (i * num[i] % p * cnt[c][x]) % p) % p; sum[c][x] = (sum[c][x] + (i * num[i]) % p) % p; isum[c][x] = (isum[c][x] + num[i]) % p; pos[c][x] = (pos[c][x] + i) % p; cnt[c][x]++; } cout << ans << endl; return 0; } |
✅ 100% 正确
✅ 时间复杂度最优 O(n)
✅ 蓝桥杯 / 天梯赛 标准满分写法