【GDOI2016模拟7.10】Banner

来源:互联网 发布:傲剑境界升级数据 编辑:程序博客网 时间:2024/06/03 22:57

Description

给定一个网格,左下角为(0,0),右上角为(n,m),求有多少种方案可以选择两个整点点,使得这两个点的连线不经过其他整点并且长度在l~r之间。答案对p取模。
n,m<=10^5,1<=l<=r<=1.5*10^4,p<=10^9

Solution

首先,让我们来想一想,怎样的两点连线不经过其他点呢?
构成直角三角形,两条直角边长互质!
于是题目就相当于求

i=1nj=1,l2<=i2+j2<=r2me(gcd(i,j))(ni+1)(mj+1)

考虑优化
Ans=i=1min(n,r)(ni+1)j=max(l2i2,1)min(r2i2,m)e(gcd(i,j))(mj+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);}

(好烦哪!!好丑啊!!)

1 0