前言
结论题我只能orz。
题解错了很不舒服。
题意
找到(n,m)以内点对欧几里得步数最大的点对个数。
做法
设f(a,b)表示欧几里得步数。
我们先设斐波那契数
F0=1,F1=1,Fi+2=Fi+1+Fi
容易发现
f(Fk,Fk+1)=k
。
结论一:如果
f(x,y)=k
,且
x<y
,则一定有
x>=Fk
以及
y>=Fk+1
。
这个可以用归纳法证明。
然后我们发现,对于
gcd(x,y)>1
如果
f(x,y)>1
这样的(x,y)不会计算入答案。
这个也好证。
于是我们只需要在答案等于1的时候特判,其余情况当然只需要考虑gcd为1的点对。
我们定义如果不存在
x′<x和y′<y使得f(x′,y′)>f(x,y)
那么(x,y)是好的。
显然我们只统计好的点对。
然后我们定义如果
f(x,y)=k
且
x,y<=Fk+2+Fk−1
那么(x,y)是优秀的。
结论二:如果(x,y)是好的,那么一步后会变成优秀的。
可以用反证法证明。
然后我们只需要找出所有优秀的点对,就很容易统计答案。
找的方法是用k的得到k+1的。
优秀的点对有多少呢?
在最小的情况下是
(Fk,Fk+1)
,容易发现它只会产生2~3个新的,而其余的则只能产生1个。
因此是log级别的。
#include<cstdio>
#include<algorithm>
#include<vector>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pi;
const int mx=90+10,mo=1000000007;
const ll inf=1000000000000000000;
ll fib[mx];
vector<pi> a[mx];
vector<pi>::iterator it;
ll x,y,n,m,num;
int i,j,k,l,t,tot,ca,ans;
int main(){
fib[0]=fib[1]=1;
tot=1;
while (1){
fib[tot+1]=fib[tot]+fib[tot-1];
if (fib[tot+1]>inf) break;
tot++;
}
a[1].pb(mp(1,2));a[1].pb(mp(1,3));a[1].pb(mp(1,4));
fo(i,1,tot-3){
it=a[i].begin();
while (it!=a[i].end()){
x=(*it).xx;y=(*it).yy;
x+=y;
while (x<=fib[i+3]+fib[i]){
a[i+1].pb(mp(y,x));
x+=y;
}
it++;
}
}
scanf("%d",&ca);
while (ca--){
scanf("%lld%lld",&n,&m);
if (n>m) swap(n,m);
ans=1;
while (fib[ans+1]<=n&&fib[ans+2]<=m) ans++;
printf("%d ",ans);
if (ans==1){
printf("%lld\n",n*m%mo);
continue;
}
else{
num=0;
it=a[ans-1].begin();
while (it!=a[ans-1].end()){
x=(*it).xx;y=(*it).yy;
if (y<=n) (num+=(m-x)/y)%=mo;
if (y<=m) (num+=(n-x)/y)%=mo;
it++;
}
printf("%d\n",num);
}
}
}