P2671 [NOIP2015 普及组] 求和(简单数学,前缀和)

我们发现规律是  x到y的距离==y到z的距离

并且x的颜色==z的颜色

所以我们只需要枚举y的位置就可以了,然后再想办法优化,暴力代码如下

时间复杂度是O(N^2)

for (int k = 2;k <= n;k++)
	{//选择一个点作为y
		int i = k - 1, j = k + 1;
		while (i >= 1 && j <= n)
		{
			if(c[i]==c[j])
				res = (res + (i + j) * (a[i]+a[j])) % mod;
			i--, j++;
		}
	}

怎么优化? 

优化其实就是要发现一些关于题目的性质

贪心,前缀和,差分,数学...都可以被用来优化

我们发现一个点z如果存在另一个点x满足性质

我们发现性质

1.三元组的取值和y无关,只与x,z有关

2.x,z的颜色相同,并且他们的奇偶性相同(因为要确保中间有奇数个点)

3.三元组的寻找可以进行优化,如下

三元组的优化

由于三元组的计算和y无关,所以我们只需要枚举z元素,然后找到在他之前所有可以取的x元素就可以了

更新res操作如下

	res = (res + (ll)cnt[col][i % 2][0] * a[i]) % mod;		//Σx*num_z
		res = (res + (ll)cnt[col][i % 2][1] * i) % mod;			//Σnum_x*z
		res = (res +(ll) cnt[col][i % 2][2]) % mod;				//Σnum_x*x
		res = (res + (ll)cnt[col][i % 2][3] * i * a[i]) % mod;	//z*num_z

 更新前缀和数组如下

cnt[col][i % 2][0] = (cnt[col][i % 2][0] + i) % mod;
		cnt[col][i % 2][1] = (cnt[col][i % 2][1] + a[i]) % mod;
		cnt[col][i % 2][2] = (cnt[col][i % 2][2] +(ll) i * a[i]) % mod;
		cnt[col][i % 2][3]++;

 完整代码如下

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100050;
const int mod = 10007;
int a[N];	//数据数组
int c[N];	//颜色数组
typedef long long ll;
ll cnt[N][2][4];

ll res;
int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1;i <= n;i++)
		cin >> a[i];	//输入数据
	for (int i = 1;i <= n;i++)
		cin >> c[i];	//输入颜色
	
	//处理前缀和
	//注意要颜色相同,奇偶性相同
	for (int i = 1;i <= n;i++)
	{
		//以下是更新res值
		int col = c[i];
		//********注意这里的乘积可能超过1e9,一定要全部开ll,否则会wa掉
		res = (res + (ll)cnt[col][i % 2][0] * a[i]) % mod;		//Σx*num_z
		res = (res + (ll)cnt[col][i % 2][1] * i) % mod;			//Σnum_x*z
		res = (res +(ll) cnt[col][i % 2][2]) % mod;				//Σnum_x*x
		res = (res + (ll)cnt[col][i % 2][3] * i * a[i]) % mod;	//z*num_z
		//[3]表示n
		//以下是更新前缀和数组
		cnt[col][i % 2][0] = (cnt[col][i % 2][0] + i) % mod;
		cnt[col][i % 2][1] = (cnt[col][i % 2][1] + a[i]) % mod;
		cnt[col][i % 2][2] = (cnt[col][i % 2][2] +(ll) i * a[i]) % mod;
		cnt[col][i % 2][3]++;
	}
	cout << res % mod;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值