题目描述:
LZN 搞完保送生考试,终于要回到信息组大家庭了,Chanxer 决定要好好地欢迎LZN,于是他在在操场上整齐地插了(M + 1) *(N + 1) 个标杆,形成了一个平面直角坐标系,左下角的标杆的坐标为(0; 0),右上角的标杆的坐标为(M;N),Chanxer 现在想要选择两个标杆作为端点连上横幅“ 机房欢迎你”。
可是,由于Chanxer 很农,他不希望横幅被其它的标杆拦住,因此他要求选择的两个标杆的连线不应该经过其它标杆,并且横幅的长度还应该在[L;R]以内。
现在Chanxer 想要知道他有多少种选法,注意,由于横幅的两面是一模一样的,所以选择的两个点没有起点终点之分,鉴于答案可能很大,而又不允许上交Python、Java 等语言的源代码,你只需要告诉他答案除以B 的余数是多少就可以了。
题目分析:
我们设两个两个标杆横坐标的距离差为x,纵坐标的距离差为y。
我们把x=0和y=0的情况刨出来单算:
只有L<=1的时候,这两种情况才有答案,答案为n*(m+1)+m *(n+1)
剩下的就是:
解释一下这个式子的意思:枚举x和y,答案加上满足长度在范围内且连线不经其他过格点(即gcd(x,y)==1)的方案。
对于一对固定的x和y,它的在横向上最多可以取(m-x+1)个位置,在纵向上最多可以取(n-y+1)个位置,那么可选的方案就共有(m-x+1)*(n-y+1)种,但是这条线段可以向上倾斜也可以向下倾斜,所以答案要乘2。
我们只需要枚举x和y,当然如果暴力枚举的话肯定GG。
观察这个式子,可以观察出,x只需要枚举到min(m,R),而当x固定的时候,y只需要在区间[sqrt(L* L-x *x),sqrt(R *R-x *x) ]内枚举即可,即可以省掉长度范围的判断。
按照上述条件把原式进行变形:
我们设 l 为枚举y的下限,r为枚举y的上限。
于是问题转化成了,如何求:
即如何快速求出:
(lim和x是给定的)
这个时候可以利用莫比乌斯函数进行容斥,即该式可以转化为:
上面这个转化不懂的可以选择看看我的Zap的题解:
http://blog.csdn.net/todobe/article/details/60579910
也可以根据莫比乌斯函数求和的性质,只有gcd为1的时候莫比乌斯函数的和为1,其他时候都为0,乘上方案数相加之后恰好为所求答案。
这样我们只要用sqrt(x)的时间枚举x的约数,然后∑(d|y)(n-y+1)是可以O(1)算出的。
不会O(1)算的小伙伴看这里:
我们设一个临时变量t=lim/d(因为是要求lim范围内合法的y嘛)
这个t代表共有t个合法的y,分别为:d,2d,3d……td;
我们要求的就是把这些y分别带入(n-y+1)里面然后求和。
这样我们把常量(n+1)提出来,就是(n+1)* t +((1+t)* t /2)*d
这样就算出来了(你们看完之后是不是觉得写题解的人非常智障啊=。=)
时间复杂度O(nsqrt(n))
其实后面的部分也可以不用莫比乌斯函数来做,直接容斥也可以。
(但是本宝宝是蒟蒻T^T,只会这种)
注意:
在计算第二维的下限的时候要和1取一个max,因为0的情况你已经单算了,如果枚举的x(即我代码里的i)已经大于L的时候,它的下限直接设为1即可
代码如下:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#define N 120000
using namespace std;
typedef long long LL;
inline LL sqr(LL x) { return x*x; }
LL n,m,L,R,mod,ans;
inline LL sum(LL x) { return (x*(x+1)>>1)%mod; }
bool mark[N];
LL pri[N],u[N];
int top;
void init()
{
u[1]=1;
for(LL i=2;i<N;i++)
{
if(!mark[i])
{
pri[++top]=i;
u[i]=-1;
}
for(int j=1;j<=top && pri[j]*i<N;j++)
{
mark[i*pri[j]]=true;
if(i%pri[j]==0)
{
u[i*pri[j]]=0;
break;
}
u[i*pri[j]]=-u[i];
}
}
}
LL calc(LL lim,LL x)
{
LL ans=0;
LL t,i;
for(i=1;i*i<x;i++)
{
if(x%i==0)
{
t=lim/i;
ans+=(u[i]*((n+1)*t%mod-sum(t)*i%mod)+mod)%mod,ans%=mod;
t=lim/(x/i);
ans+=(u[x/i]*((n+1)*t%mod-sum(t)*(x/i)%mod)+mod)%mod,ans%=mod;
}
}
if(i*i==x)
{
t=lim/i;
ans+=(u[i]*((n+1)*t%mod-sum(t)*i%mod)+mod)%mod,ans%=mod;
}
return ans;
}
int main()
{
init();
cin>>n>>m>>L>>R>>mod;
for(LL i=1;i<=min(m,R);i++)
{
LL l=i>L?1:max(1ll,LL(ceil(sqrt(sqr(L)-sqr(i)))));
LL r=min(n,LL(sqrt(sqr(R)-sqr(i))));
if(l<=r) ans+=(calc(r,i)-calc(l-1,i)+mod)%mod*(m-i+1)%mod*2%mod,ans%=mod;
}
if(L<=1) ans+=m*(n+1)%mod+n*(m+1)%mod,ans%=mod;
cout<<ans<<endl;
return 0;
}