【取模注意】AtCoder - abc177_c Sum of product of pairs

题目链接

本题题意是对每对数字求乘积,然后对所有乘积求和。

容易想到用前缀和去处理,用第i个数乘以【第i+1之后所有数的和】,对每个i这样操作,得到的数再求和,就是最终的答案。

但问题在于,数字可能很大,需要取模。

【模运算的性质】

模运算与基本四则运算有些相似,但是除法例外。其规则如下:

  1. (a + b) % p = (a % p + b % p) % p (1)

  2. (a - b) % p = (a % p - b % p) % p (2)【注意:两个取模的结果进行相减操作时(易知前面的数理论上应该大,但取模之后可能小于后面的数)此时应该加上MOD或其倍数,再对相减结果进行取模,从而保证输出为正】

  3. (a * b) % p = (a % p * b % p) % p (3)

  4. a ^ b % p = ((a % p)^b) % p (4)

最初的错误代码是:
 

#include<stdio.h>
typedef long long ll;
ll a[200005];
ll num[200005];
const int mod = 1000000007;
int n;
int main(){
	scanf("%d",&n);
	ll ans = 0;
	for(int i = 1;i <= n;i++){
		scanf("%lld",&num[i]);
		a[i] = (a[i-1] + num[i])%mod;
	}
	
	for(int i = 1;i <= n-1;i++){
		ans = (ans + num[i]*(a[n]-a[i]))%mod;
	}
	
	printf("%lld\n",ans);
}

错误原因:没有考虑相减可能负数;取余不充分

首先应该注意的是:

对于

           a[i] = (a[i-1] + num[i])%mod;

而言,这样求出来的前缀和就不再是原来意义上的前缀和,因为取模之后,后面的数可能比前面的数小,这时候正确的做法是在下面再次加mod.

所以下面要写成:

        ans = (ans + num[i]*(a[n]-a[i]+mod))%mod;

这样就能把可能是负数的a[n] - a[i]变成“正数”,且最终结果不变。

当然,为了保险起见,还要写成

        ans = (ans + (  (num[i]%mod)  *  (  (a[n]-a[i]+mod)%mod  ) )%mod;

最终AC的代码也就变成了:

#include<stdio.h>
typedef long long ll;
ll a[200005];
ll num[200005];
const int mod = 1000000007;
int n;
int main(){
	scanf("%d",&n);
	ll ans = 0;
	for(int i = 1;i <= n;i++){
		scanf("%lld",&num[i]);
		a[i] = (a[i-1] + num[i])%mod;
	}
	
	for(int i = 1;i <= n-1;i++){
		ans = (ans + ((num[i]%mod)*((a[n]-a[i]+mod)%mod))%mod)%mod;
/*用到的性质:
(a*b) % p = (a%p * b%p)%p,每一次这个小部分都满足性质相等
所有数相加后取余,等于【当前加数】加上前面所有数求和取余的结果,然后再最终取余。这样不会超出long long范围,而且结果正确
*/
	}
	
	printf("%lld\n",ans);
}

 

当然,最正常的思路应当是:

#include<stdio.h>
typedef long long ll;
ll a[200005];
ll num[200005];
const int mod = 1000000007;
int n;
int main(){
	scanf("%d",&n);
	ll ans = 0;
	for(int i = 1;i <= n;i++){
		scanf("%lld",&num[i]);
		a[i] = a[i-1] + num[i];//实际上long long最多是10^14,可以存储,无需取模
	}
	
	for(int i = 1;i <= n-1;i++){
	    ans = (ans + (  ((num[i])%mod)  *  (((a[n]-a[i]))%mod)  ) %mod ) %mod;
/*用到的性质:
(a*b) % p = (a%p * b%p)%p,每一次这个小部分都满足性质相等
所有数相加后取余,等于【当前加数】加上前面所有数求和取余的结果,然后再最终取余。这样不会超出long long范围,而且结果正确
*/
	}
	
	printf("%lld\n",ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值