Description

LZN 搞完保送生考试,终于要回到信息组大家庭了,Chanxer 决定要好好地欢迎LZN,于是他在在操场上整齐地插了(M + 1) *(N + 1) 个标杆,形成了一个平面直角坐标系,左下角的标杆的坐标为(0; 0),右上角的标杆的坐标为(M;N),Chanxer 现在想要选择两个标杆作为端点连上横幅“ 机房欢迎你”。
可是,由于Chanxer 很农,他不希望横幅被其它的标杆拦住,因此他要求选择的两个标杆的连线不应该经过其它标杆,并且横幅的长度还应该在[L;R]以内。
现在Chanxer 想要知道他有多少种选法,注意,由于横幅的两面是一模一样的,所以选择的两个点没有起点终点之分,鉴于答案可能很大,而又不允许上交Python、Java 等语言的源代码,你只需要告诉他答案除以B 的余数是多
少就可以了。

Solution

题意是

∑i=1n∑j=1m(l∗l<=i∗i+j∗j<=r∗r,gcd(i,j)=1)(n−i+1)(m−i+1)


(i,j都是长度)


我们枚举i之后,根据范围枚举j


∑i=1n(n−i+1)∑j=max(1,l∗l−i∗i√)min(m,r∗r−i∗i√)(l>i,上标>下标,gcd(i,j)=1)(m−i+1)


我们要找的是互质的,那么我们可以用总数减去不互质的


∑i=1n(n−i+1)∗(suan(r∗r−i∗i−−−−−−−−−√)−suan(⌈l∗l−i∗i−−−−−−−−√⌉−1))


我们的suan(x)就是计算1到x里面与x互质的答案


用容斥原理暴力处理就好了。


注意c++里面位运算得优先级,非常低。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
typedef long long ll;
const int maxn=100;
using namespace std;
ll i,j,k,l,t,n,m,mo,r,a[maxn];
ll ans;
ll sqr(ll x){
return x*x;
}
ll chuli(ll x){
return x*(x+1)/2%mo;
}
ll suan(ll x){
ll i,j;
ll w=((m+1)*x%mo-chuli(x)+mo)%mo;
fo(i,1,(1<<a[0])-1){
ll o=1,p=1;
fo(j,1,a[0]){
if((i&(1<<(j-1))))p*=a[j],o=-o;
}
if(p>x)continue;
w=(w+o*(((m+1)*(x/p)%mo-chuli(x/p)*p%mo+mo)%mo+mo))%mo;
}
return w;
}
int main(){
scanf("%lld%lld%lld%lld%lld",&n,&m,&l,&r,&mo);
fo(i,1,min(n,r)){
a[0]=0;t=i;
fo(j,2,sqrt(i)){
if(t%j==0){
a[++a[0]]=j;
while(t%j==0)t=t/j;
}
}
if(t!=1)a[++a[0]]=t;
if(l>=i)k=max(ll(1),ll(ceil(sqrt(sqr(l)-sqr(i)))));else k=1;
t=min(m,ll(sqrt(sqr(r)-sqr(i))));
if(k<=t)ans=(ans+(suan(t)-suan(k-1)+mo)%mo*(n-i+1)%mo*2%mo)%mo;
}
if(l<=1){
ans=(ans+m*(n+1)%mo+n*(m+1)%mo)%mo;
}
printf("%lld\n",ans);
}