洛谷 P2671 [NOIP2015 普及组] 求和

文章讲述了如何解决NOIP2015普及组的一道题目,通过将三元组条件转化为寻找颜色相同且下标和为偶数的数对,利用map存储颜色相同的数,然后对奇偶性进行讨论,分别计算奇数和偶数的贡献,最后进行模运算得到总和。代码中展示了具体实现过程。

原题链接:[NOIP2015 普及组] 求和 - 洛谷

 题目要求我们计算所有满足条件的三元组总分,仔细观察三元组所需要满足的第一个条件:x < y < z, y - x = z - y,我们可以化为x + z = 2y,而三元组需要满足的第二个条件中不包含y,所以不难发现第一个条件可以转化为x + z为偶数。题目就转化为了寻找下标(从1开始)之和为偶数并且颜色相同的两个数,这样的两个数一定满足三元组的两个条件,将x,z(找到的两个数的下标)代入题目表达式,将所有结果相加就得到了答案。

所以我们只需要将所以颜色相同的数存放到一起,再从中找出所有符合上述条件数就可以了。这里可以使用map来将所有颜色相同的数存放到一起:

int co[100005]; // co代表该位置的颜色
map<ll, vector<ll>> mp;
for (int i = 1; i<= n; i++){
	cin >> co[i];
	mp[co[i]].push_back(i);
}

我们就可以将颜色相同的下标存放到同一个vector当中,后续需要访问就可以直接进行遍历。

 再观察题目数据范围,使用双重循环来读取每一对数字的时间复杂度O(n^{2})肯定会超时。我们可以先列举部分情况来观察是否具有某种规律:

假设总共有2种颜色(ai表示下标,a2表示下标为2):
        颜色1:a1,a2,a3,a5。

        颜色2:a4,a6。

对于颜色1:总和为(a1 + a3) * (num1+num3) + (a1 + a5) * (num1 + num5) + (a3 + a5) * (num3 + num5) = a1 * (num1 + num2 +num3) + a3 * (num1 + num2 +num3) + a5 * (num1 + num2 +num3) + a1 * num1 + a3 * num3 +a5 * num5;

对于颜色2:总和为(a4 + a6) * (num4 + num6) = a4 * (num4 + num6) + a6 * (num4 + num6);

相加为偶数有两种情况:奇 + 奇 / 偶 + 偶。所以对于相同颜色,我们可以对其中的所有元素分奇偶讨论。对于上述颜色1,(num1 + num2 +num3)为奇数和,所以总和为a1 * sum1(奇数和) + a3 * sum1 + a5 * sum1 + a1 * num1 + a3 * num3 + a5 * num5,其实只需要在颜色1中再加入几个几个奇数就可以更加明显的得出后半部分的规律(留下思考余地),后面部分其实是(奇数个数 - 2) * ai * numi,然后相加。所以可以得出:对于某种颜色,将其中的所有元素分奇偶单独存储,对所有的奇数进行遍历,ans(总分) += (ai * sum1(奇数和) % mod + (奇数个数 - 2) * ai * numi % mod),偶数操作相同。对所有颜色进行相同操作即可得到最后答案(注意取模)。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 6;
int num[N], co[N];
int mod= 1e4 +7;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n, m;
	cin >> n >> m;
	ll ans = 0;
	for (int i = 1; i <= n; i++){
		cin >> num[i];
	}
	map<ll, vector<ll>> mp;
	for (int i = 1; i<= n; i++){
		cin >> co[i];
		mp[co[i]].push_back(i);
	}
	for (auto x : mp){
		auto v = x.second;
		vector<int> a, b;
		ll sum1=0, sum2=0;
		for (int i = 0; i < v.size(); i++){
			if (v[i] % 2){
				a.push_back(v[i]);
				sum1 += num[v[i]];
			} else{
				b.push_back(v[i]);
				sum2 += num[v[i]];
			}
		}
		for (auto y : a){
			ans += ((a.size() - 2) * y * num[y] + y * sum1) % mod;
			ans %= mod;
		}
		for (auto y : b){
			ans += ((b.size() - 2) * y * num[y] + y * sum2) % mod;
			ans %= mod;
		}
	}
	cout << ans;
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值