3148. 【GDKOI2013】琪露诺的完美算法课
Description
琪露诺的完美算法课开课啦!今天上的是几何课哦!
“直角三角形就是有一个角是 90 度的三角形哦!而且假如最长的那条边边长是 c,剩下的两条边长度分别是a 和 b,它们就一定会满足 a^2+b^2=c^2 哦!比如 (3,4,5) 哦!还有 (4,3,5) 哦!还有 (4,7,8) 哦!”琪露诺戴着眼镜挥着教鞭说道。
“笨蛋,(4,7,8) 是直角三角形吗?”不知从哪里传来的声音。
于是琪露诺算了一下,发现 4^2+7^2=65<>64。
“你...你才是笨蛋呢!比那边大 1 的话...也和直角三角形差不多!都是直角三角形!”
“那对于三条边边长都为整数的三角形 (a,b,c),如果 a 在某一个范围内,你所谓的‘直角三角形’有多少个呢?”天之音继续提问到。
你能帮他算出来吗?
Solution
先给出一些引理:
-
若用 \(d(x)\) 表示 \(x\) 的因数个数,并且 \(x=\Pi {p_i}^{c_i}\)(其中 \(p_i\) 都是质数),那么 \(d(x)=\Pi(c_i+1)\)。
证明:每个质因数有 \(c_i+1\) 种选择,不同的选择可以组成不同的因数,用乘法原理易证。
-
若 \(n=x\times y[\gcd(x,y)=1]\),那么 \(d(n)=d(x)\times d(y)\)。
证明:\(x\) 和 \(y\) 都是 \(n\) 的因数,所以组成 \(n\) 的质因数 \(x\) 或 \(y\) 中有且仅有一个全部拥有(因为互质),再根据乘法结合律可以证明。
有了以上两个引理后,我们考虑对题目中的条件进行转换。
已知 \(a^2+b^2=c^2\) 或者 \(a^2+b^2-1=c^2\),那么分开来做。
移项并因式分解得 \(a^2=(c+b)(c-b)\) 或者 \((a+1)(a-1)=(c+b)(c-b)\),而 \(c+b\) 和 \(c-b\) 同奇同偶,所以就是要将 \(a^2\) 或者 \((a+1)(a-1)\) 分成同奇同偶的因数。
先考虑简单版:
设 \(f(x)\) 表示将 \(x\) 分成同奇同偶的因数的方案数(\(x\le 10^7\))。
假如 \(x\) 是奇数,那么只需要将 \(x\) 的因数中任取一个即可,所以 \(f(x)=\frac{d(x)}{2}\)。
假如 \(x\) 是偶数,设 \(x=2^y\times p\),而 \(p\) 是个奇数,所以我们可以先选择 \(p\) 的因数,再乘上一定量的 2。为了保证 2 在分成的两个因数中都存在,所以 \(y\ge 2\),因此 \(x\) 一定要是 4 的倍数,如果不是 \(f(x)=0\)。
当 \(y\ge 2\) 时,\(f(x)=\frac{d(p)\times(y-1)}{2}=\frac{d(p)\times d(2^{y-2})}{2}=\frac{d(p\times 2^{y-2})}{2}=\dfrac{d(\frac{x}{4})}{2}\)。
现在考虑求 \(f(x^2)\)(\(x\le 10^7\))。首先若 \(x=\Pi {p_i}^{c_{i}}\),那么 \(x^2=\Pi {p_i}^{2c_i}\),所以 \(d(x^2)=\Pi(2c_i+1)\)。
那么接下来和求 \(f(x)\) 类似。若 \(x\) 为奇数,\(f(x^2)=\frac{d(x^2)}{2}\)。若 \(x\) 为偶数,\(f(x^2)=\dfrac{d(\frac{x^2}{4})}{2}=\dfrac{d(\frac{x}{2})}{2}\)(注意偶数的时候没有 \(x\) 是 4 的倍数这条限制,因为只要 \(x\) 是偶数,那么 \(x^2\) 一定是 4 的倍数)。
到此我们解决了 \(a^2\) 这一部分,下面处理 \((a+1)(a-1)\)。
如果 \(a\) 是偶数,那么 \(a+1\) 和 \(a-1\) 一定互质。此时 \(d((a+1)(a-1))=d(a+1)\times d(a-1)\),然后就可以求 \(f(x)\)(\(x\) 是奇数)的做法求出 \(f((a+1)(a-1))\)。
如果 \(a\) 是奇数,那么 \(a+1\) 和 \(a-1\) 都是偶数,并且 \(\gcd(a+1,a-1)=2\),所以 \((a+1)(a-1)\) 是 4 的倍数,因此 \(f((a+1)(a-1))=\dfrac{d(\frac{(a+1)(a-1)}{4})}{2}=\dfrac{d(\frac{a+1}{2}\times \frac{a-1}{2})}{2}=\dfrac{d(\frac{a+1}{2})\times d(\frac{a-1}{2})}{2}\)。
至此两部分都处理完。而 \(d(x)\) 和 \(d(x^2)\) 可以用欧拉筛在 \(\mathcal O(n)\) 的时间内预处理出,然后枚举 \(a\),对于每个 \(a\),求答案是 \(\mathcal O(1)\) 的,因此总复杂度 \(\mathcal O(n)\)。
Code
#include<cstdio>
#define ll long long
#define N 10000005
using namespace std;
int l,r,a,b,ans,prime[N],d[N],d2[N],cnt[N];
bool bj[N];
ll x;
int main()
{
d[1]=1;d2[1]=1;
for (int i=2;i<=N;++i)
{
if (!bj[i])
{
prime[++prime[0]]=i;
d[i]=2;
d2[i]=3;
cnt[i]=1;
}
for (int j=1;j<=prime[0];++j)
{
if (prime[j]*i>N) break;
bj[prime[j]*i]=true;
if (i%prime[j]==0)
{
d[i*prime[j]]=d[i]/(cnt[i]+1)*(cnt[i]+2);
d2[i*prime[j]]=d2[i]/(2*cnt[i]+1)*(2*(cnt[i]+1)+1);
cnt[i*prime[j]]=cnt[i]+1;
break;
}
else
{
d[i*prime[j]]=d[i]*d[prime[j]];
d2[i*prime[j]]=d2[i]*d2[prime[j]];
cnt[i*prime[j]]=1;
}
}
}
scanf("%d%d",&l,&r);
for (int i=l;i<=r;++i)
{
if (i%2==1) ans+=d2[i]/2;
if (i%2==0) ans+=d2[i/2]/2;
if (i%2==1) ans+=d[(i+1)/2]*d[(i-1)/2]/2;
if (i%2==0) ans+=d[i+1]*d[i-1]/2;
}
printf("%d\n",ans);
return 0;
}