[CSP-S模拟测试]:求和(数学)

题目传送门(内部题107)


输入格式

  一行五个正整数$x_1,y_1,x_2,y_2,m$


输出格式

  输出一个整数,为所求的答案对$m$取模后的结果。


样例

样例输入:

2 1 5 3 10007

样例输出:

54


数据范围与提示

  对于$20\%$的数据,满足$x_2,y_2,m\leqslant 1,000$。
  对于$40\%$的数据,满足$x_2,y_2,m\leqslant 100,000$。
  对于$70\%$的数据,满足$x_2,y_2,m\leqslant 10^9$。
  对于$100\%$的数据,满足$1\leqslant x_2,y_2,m\leqslant 10^{18},1\leqslant x_1\leqslant x_2,1\leqslant y_1\leqslant y_2$。


题解

先来化式子,对于$\sum \limits_{i=x_1}^{x_2}\sum \limits_{i=y_1}^{y_2}d_{i,j}$,其左上角为$x_1+y_1-1$,那么其右上角为$x_1+y_1+(y_2-y_1)-1$。

不妨设$y_2-y_1+1=len$。

那么根据等差数列求和公式,第一行的和就是$\frac{[2\times (x_1+y_1-1)+len-1]\times len}{2}$,因为需要取模,而模数不是质数,我们又不想打$CRT$;但是我们发现如果$len$是一个奇数,那么$[2\times (x_1+y_1-1)+len-1]$就可以除$2$,否则$len$就可以除$2$。

现在我们求出了$\sum \limits_{i=y_1}^{y_2}d_{x_1,i}$,不妨设其为$sum$,现在接着来考虑$\sum \limits_{i=x_1}^{x_2}\sum \limits_{i=y_1}^{y_2}d_{i,j}$该如何求。

发现每行之间的差也呈等差数列,且公差为$len$(因为每一个数都比上一行大$1$),那么最后一行的和就是$sum+(x_2-x_1)\times len$,再根据等差数列求和公式得到答案为$\frac{[2\times sum+(x_2-x_1)\times len]\times (x_2-x_1+1)}{2}$,同样可以根据上面的方法避免逆元。

但是发现可能会乘爆$long\ long$,一种避免高精的方法就是使用慢速乘,附一份代码:

long long qmul(long long x,long long y)
{
	long long res=0;
	while(y)
	{
		if(y&1)res=(res+x)%mod;
		x=(x+x)%mod;
		y>>=1;
	}
	return res;
}

时间复杂度:$\Theta(\log m)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
long long x,y,x2,y2,m;
long long qmul(long long x,long long y)
{
	long long res=0;
	while(y)
	{
		if(y&1)res=(res+x)%m;
		x=(x+x)%m;
		y>>=1;
	}
	return res;
}
int main()
{
	scanf("%lld%lld%lld%lld%lld",&x,&y,&x2,&y2,&m);
	long long len=y2-y+1,sum,ans;
	if(len&1)sum=qmul(((x+y-1)+((len-1)>>1))%m,len);
	else sum=qmul((qmul(x+y-1,2)+len-1)%m,len>>1);
	if((x2-x)&1)ans=qmul((qmul(sum,2)+qmul(x2-x,len))%m,(x2-x+1)>>1);
	else ans=qmul(sum+qmul((x2-x)>>1,len),x2-x+1);
	printf("%lld",ans);
	return 0;
}

rp++

posted @ 2019-11-01 09:18  HEOI-动动  阅读(172)  评论(0编辑  收藏  举报