【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);
}

 

posted @ 2018-07-02 19:13  lnyzo  阅读(84)  评论(0编辑  收藏  举报