题目传送门(内部题107)
输入格式
一行五个正整数$x_1,y_1,x_2,y_2,m$
输出格式
输出一个整数,为所求的答案对$m$取模后的结果。
样例
样例输入:
2 1 5 3 10007
样例输出:
54
数据范围与提示
对于$20\%$的数据,满足$x_2,y_2,mleqslant 1,000$。
对于$40\%$的数据,满足$x_2,y_2,mleqslant 100,000$。
对于$70\%$的数据,满足$x_2,y_2,mleqslant 10^9$。
对于$100\%$的数据,满足$1leqslant x_2,y_2,mleqslant 10^{18},1leqslant x_1leqslant x_2,1leqslant y_1leqslant 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 imes (x_1+y_1-1)+len-1] imes len}{2}$,因为需要取模,而模数不是质数,我们又不想打$CRT$;但是我们发现如果$len$是一个奇数,那么$[2 imes (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) imes len$,再根据等差数列求和公式得到答案为$frac{[2 imes sum+(x_2-x_1) imes len] imes (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++