【GDOI2016模拟7.10】Banner
Description
给定一个网格,左下角为(0,0),右上角为(n,m),求有多少种方案可以选择两个整点点,使得这两个点的连线不经过其他整点并且长度在l~r之间。答案对p取模。
n,m<=10^5,1<=l<=r<=1.5*10^4,p<=10^9
Solution
首先,让我们来想一想,怎样的两点连线不经过其他点呢?
构成直角三角形,两条直角边长互质!
于是题目就相当于求
考虑优化,这里是用了 了不起( ⊙ o ⊙ )的容斥
(1~x的和) -(至少含有一个当前因子的)+......-+-+-+..
#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); }