Description
给定一个网格,左下角为(0,0),右上角为(n,m),求有多少种方案可以选择两个整点点,使得这两个点的连线不经过其他整点并且长度在l~r之间。答案对p取模。
n,m<=10^5,1<=l<=r<=1.5*10^4,p<=10^9
Solution
首先,让我们来想一想,怎样的两点连线不经过其他点呢?
构成直角三角形,两条直角边长互质!
于是题目就相当于求
∑i=1n∑j=1,l2<=i2+j2<=r2me(gcd(i,j))(n−i+1)∗(m−j+1)
考虑优化
Ans=∑i=1min(n,r)(n−i+1)∑j=max(⌈l2−i2√⌉,1)min(⌊r2−i2√⌋,m)e(gcd(i,j))(m−j+1)
发现那一坨东西可以用容斥原理搞出来。
直接暴力容斥就好了,因为i的质因数个数不会特别多。
%%%Yves___的神奇容斥法(涨姿势了)
注意边界。
Code
#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define sqr(x) (x)*(x)
using namespace std;
typedef long long ll;
ll n,m,l,r,p,ans,a[20];
int gcd(int x,int y) {return (y)?gcd(y,x%y):x;}
ll sum(ll x) {return x*(x+1)/2%p;}
ll calc(ll x) {
if (x<0) return 0;
ll ans=((m+1)*x%p-sum(x)+p)%p;
fo(i,1,(1<<a[0])-1) {
ll bz=1,k=1;
fo(j,1,a[0]) if (i&(1<<(j-1))) bz=-bz,k*=a[j];
if (k>x) continue;
ans=(ans+bz*(((m+1)*(x/k)%p-k*sum(x/k)%p)+p)%p+p)%p;
}
return ans;
}
int main() {
scanf("%lld%lld%lld%lld%lld",&n,&m,&l,&r,&p);
if (l<=1) ans=((n+1)*m%p+n*(m+1)%p)%p;
fo(i,1,min(n,r)) {
ll le,ri;a[0]=0;ll k=i;
fo(j,2,ll(sqrt(i))) {
if (!(k%j)) a[++a[0]]=j;
while (!(k%j)) k/=j;
}
if (k!=1) a[++a[0]]=k;
if (i<=l) le=max(ll(ceil(sqrt(sqr(l)-sqr(i)))),ll(1));else le=1;
ri=min(m,ll(sqrt(sqr(r)-sqr(i))));
if (le<=ri) ans=(ans+(calc(ri)-calc(le-1)+p)%p*(n-i+1)*2%p)%p;
}
printf("%lld",ans);
}
(好烦哪!!好丑啊!!)