[agc015f]Kenus the Ancient Greek

来源:互联网 发布:机械结构优化设计 编辑:程序博客网 时间:2024/06/10 01:03

前言

结论题我只能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<xy<y使f(x,y)>f(x,y)那么(x,y)是好的。
显然我们只统计好的点对。
然后我们定义如果f(x,y)=kx,y<=Fk+2+Fk1那么(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);        }    }}