[AGC015F] Kenus the Ancient Greek

tag:数论,构造


题意

给定 \(X,Y\),设 \(g(x,y)\) 为 对 \(x,y\) 用辗转相除法求gcd需要的步数。求 \(gcd(x,y),\ x\le X,\ y\le Y\) 的最大值,和能取到最大值的 \((x,y)\) 的对数。

\(Q\le300000,\ X,Y\le10^{18},\ mod=10^9+7\)


题解

最大值很好求,考虑倒推,就是让每一步的两个数尽量小,所以让“辗转相除法”变为“辗转相减”就行了。\((0,1)\to(1,2)\to(2,3)\to(3,5)\),发现这个就是斐波那契数列,所以找到最大的 \(i,\ fib(i)\le X, fib(i+1)\le Y\) 就行了。


考虑求对数。由于 \(g(a,b)=g(a,b+ka)\),所以不用考虑完所有情况,只需要考虑 \(g(a,b)=ans,\ a<b<2a\),然后可以直接用除法计算出贡献。


接下来考虑如何找出这些 \((a,b)\)

把倒推过程画出来。

对于 \(ans=3\) 的情况,只有 \((2,3)(3,4)\),倒推路径分别为:

\[0\to1\to2\to3 \]

\[0\to1\to3\to4 \]

对于 \(ans=4\) 的情况,首先可以在 \(ans=3\) 的基础上往后扩展为 \(0\to1\to2\to3\to5\)\(0\to1\to3\to4\to7\)。其次,我们还可以把第一条路径的 “\(3\)” 改大一点点,形成一条新的倒推路径:

\[0\to1\to2\to5\to7 \]

但是只能延伸出一条新的路径,因为把 “\(3\)” 改为 “\(7\)” 后,

\[0\to1\to2\to7\to9 \]

此时答案就为 \(5\) 了。(\(0\to1\to2\to3\to5\to8\)

所以推出 \(ans=4\) 时有 \((3,5)(4,7)(5,7)\)


那么这个规律能否拓展到一般情况呢?考虑从 \(ans=k\) 推出 \(ans=k+1\)

首先对于每对 \(ans=k\)\((a,b)\),显然可以推出一对 \(ans=k+1\)\((b,a+b)\)

然后类似于上面,考虑将第一条路径的结尾改大一点点,延伸出一条分支。比如 \(ans=k\) 时,第一条路径最后部分为:

\[\cdots\to a\to b \]

那么把 \(b\) 改为 \(a+b\),可以延伸出一条

\[\cdots\to a\to a+b\to 2a+b \]

这条路径显然是合理的,因为 \(2a+b<a+2b\)\(a+2b\) 指的是第一条路径的下一个元素。也就是说这条路径的加入,不会导致第一条路径变长,即不会让 \(ans\) 变大。

同样的类似于上面,无法延伸出第二条路径

\[\cdots\to a\to 2a+b\to 3a+b \]

由于第一条路径就是斐波那契数列,所以有 \(b<2a\),所以 \(3a+b>a+2b\)。也就是说如果这条路径加入了,那么第一条路径还可以加入一个元素,即 \(ans=k+2\)

而对于其他路径,就更不可能延伸出新的路径了(证明类似)。

所以 \(ans=k\) 时相对于 \(ans=k-1\) 时,只会多出一对 \((fib(k+1),fib(k+1)+fib(k-1))\)


所以得到构造方法:

  • \(ans=1\),有 \((1,2)\)
  • \(ans=k\) 时对于每个 \((a,b)\),都对应 \(ans=k+1\) 时的一个 \((b,a+b)\)
  • 同时 \(ans=k\) 还会多出一对 \((fib(k+1),\ fib(k+1)+fib(k-1))\)
fib[0] = fib[1] = 1;
for(register int i=2; fib[i-1]<=1e18; i++) fib[i] = fib[i-1]+fib[i-2];
ans[1][1] = pii(1,2);
for(register int i=2; fib[i]<=1e18; i++){
	for(register int j=1; j<i; j++) 
		ans[i][j] = pii(ans[i-1][j].second,ans[i-1][j].first+ans[i-1][j].second);
	ans[i][i] = pii(fib[i+1],fib[i+1]+fib[i-1]);
}

然后求解对数就枚举每一对元素直接计算即可。

while(ans[res+1][1].first<=x and ans[res+1][1].second<=y) res++;
for(register int i=1; i<=res; i++){
	if(ans[res][i].first<=x and ans[res][i].second<=y) sum += (y-ans[res][i].second)/ans[res][i].first+1;
	if(ans[res][i].second<=x) sum += (x-ans[res][i].second)/ans[res][i].first+1;
	sum %= MOD;
}

注意特判一下 \(ans=1\) 时,每一对 \((i,i)\) 都是合法的,所以加上 \(min(X,Y)\)


完整代码

posted @ 2021-06-26 13:46  oisdoaiu  阅读(57)  评论(0编辑  收藏  举报