2020-08-17
http://icpc.upc.edu.cn/problem.php?cid=2548&pid=2
题目描述
Rainy7 成为国王后,生活悠闲自在,她经常出门散步,观赏魔法王国的美丽风景。
她散步的地方是个充满能量的矩形,每个方格里都长满了能量花和能量草。
芳草鲜美,落英缤纷。Rainy7甚异之,复前行,欲穷其矩形。
Rainy7 散步时,会沿矩形对角线从左上角走到右下角,她能观赏到所有她经过的格子(不包括格点)中的能量花和能量草。她希望自己能观赏到Q个格子里的能量花和能量草。
Rainy7 命令 CaBeF_Yyx 找出所有能满足她的要求的矩形,否则她要用砍头术将 CaBeF_Yyx 的头砍下来。
为了不被砍头,CaBef_Yyx 很快地想出了做法,并把这题扔给了你。
Rainy7 又突发奇想,用了一个神奇的法术,将矩形变为了半径为R的圆。
将该圆的圆心看作一个长满了能量花和能量草的平面直角坐标系中的原点,Rainy7 想知道,有多少个格点落在圆上 ?
她又将这个问题交给 CaBeF_Yyx。
这次 CaBeF_Yyx 被难倒了,她不知道该如何解决,于是将这个问题扔给了你。
所以你一共要解决两个问题,才能保证 CaBeF_Yyx 不被 Rainy7 砍头。
为了拯救 CaBeF_Yyx ,请你无论如何也要做出来。
有多组数据。
输入
第一行,一个整数T ,表示询问次数。
接下来T行,每行两个整数Q,R ,意义如题。
输出
T 行,每行两个整数,表示每次询问的答案,第一个表示满足条件矩阵的数量,第二个表示圆上格点数。
样例输入
1
4 4
样例输出
4 4
提示
对于全部数据,1≤T≤10,1≤Q≤107,1≤R≤2×109
长和宽分别为X,Y的矩形与长和宽分别为Y,X的矩形是同一个矩形。
解析
参照博客:
https://blog.csdn.net/qq_30205523/article/details/100528069
https://blog.csdn.net/clover_hxy/article/details/72955869
求第一个数,m*n的矩形对角线过方格的数目为m+n-gcd(m,n)
则可列得方程,m+n-gcd(m,n)=k,提取因式gcd(m,n)记为g,则原式变为m/g+n/g-1=k/g,则m/g和n/g一定互质且和为1+k/g,此时g一定为k的约数,我们可以枚举k的约数,然后根据m/g和n/g互质,则gcd(m/g,n/g)=1,由gcd的性质,则gcd(m/g,m/g+n/g)=1,即为gcd(m/g,k/g+1)=1,互质,欧拉函数求出数目求和即可。
第二个数的求解见第二篇博客。
#include
using namespace std;
typedef long long ll;
const ll n=1e7+50;
ll pr[10005007]={0}, ph[10005007]={0};
bool vis[10005007]={0};
ll R,ans=0,S,sum=1,x,cnt=0;
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
void fun(ll d)
{
ll v;
for(ll u=1; u<=(ll)sqrt(1.0*2*R/d); u++)
{
v=(ll)sqrt(1.0*2*R/d-u*u*1.0);
if(gcd(v,u)==1&&u<=v&&d*(u*u+v*v)==2*R)
ans++;
}
}
int main()
{
ph[1] = 1 ;
for(register ll i = 2 ; i<=n-1; ++ i )
{
if(!vis[i])
{
cnt ++ ;
pr[cnt] = i ;
ph[i] = i - 1 ;
}
for(register ll j=1; j <=cnt&&i*pr[j]<=n-1; ++ j ) //欧拉筛法
{
vis[ i * pr[j] ] = 1 ;
if( i % pr[j] == 0 )
{
ph[ pr[j] * i ] = ph[i] * pr[j] ;
break;
}
else
ph [ pr[j] * i] = ph[i] * (pr[j] - 1 );
}
}
register ll t;
scanf("%lld",&t);
while(t--)
{
ans=0,sum=1;
scanf("%lld",&S);
for(register ll i=1; i<=sqrt(S); i++)
{
if(S%i==0)
{
sum+=ph[S/i+1];
if(i*i!=S)
sum+=ph[i+1];
}
}
sum/=2;
scanf("%lld",&R);
for(register ll i=1; i<=(ll)sqrt(1.0*R*2); i++)
{
if(2*R%i==0)
{
if((ll)i*i!=2*R)
{
fun(2*R/i);
fun(i);
}
else
fun(i);
}
}
ans*=4;
printf("%lld %lldn",sum,ans);
}
return 0;
}