2.求root(N,k)
题目描述
N<k时,root(N,k) = N,否则,root(N,k) = root(N',k)。N'为N的k进制表示的各位数字之和。输入x,y,k,输出root(x^y,k)的值 (这里^为乘方,不是异或),2=<k<=16,0<x,y<2000000000,有一半的测试点里 x^y 会溢出int的范围(>=2000000000)
输入描述:
每组测试数据包括一行,x(0<x<2000000000), y(0<y<2000000000), k(2<=k<=16)
输出描述:
输入可能有多组数据,对于每一组数据,root(x^y, k)的值
https://www.nowcoder.com/questionTerminal/9324a1458c564c4b9c4bfc3867a2aa66?f=discussion
这题是2010年清华考研的机试题。关于此题,网上最常见的解法是通过一些数学推导得到result = N % (k - 1) 的结论,然后用快速幂取模算法求得答案。然而,如果我们将要求的结果写成root(x, y, k)的话,其实本题还可以直接用二分递归的方法求出root(x, y, k)。也就是说,题目要我们先对x乘方,再求root函数,但实际上我们可以先求root函数,再乘方(如果结果超过k的话还要再求root函数),那么为什么可以这样做呢?
对于root(N, k)中的N,我们可以把N看作关于k的多项式,也就是N = a0 + a1*k + a2*k^2 + … + an* k^n,而我们要求的root函数就是这个多项式的系数和,也就是a0 + a1 + a2 + ... + an。下面我们考虑root(N^2, k)。此时N^2 = (a0 + a1*k + a2*k^2 + … + an* k^n)^2,而这个多项式展开后的系数和是(a0 + a1 + a2 + … + an)^2,这个结果刚好就是先对N取root函数再平方的结果。实际上,我们很容易就能看出,多项式先乘方再取系数和(先乘再去掉k)与先取系数和再乘方(先去掉k再乘),结果是一样的(因为有没有k并不影响系数间的相乘,也不影响相乘之后的求和),于是乎,我们可以得到以下的递推公式:
root(x, y, k) = root((root(x, y / 2, k))^2, 1, k), y为偶数
root(x, y, k) = root((root(x, y / 2, k))^2 * root(x, 1, k), 1, k), y为非1的奇数
root(x, 1, k) = x % k + x / k % k + ...(大于k的话再重复求root)
有了递推关系之后,我们就可以直接写出一个O(log n)的递归解法了。
#include<iostream>
using namespace std;
int root(int x,int y,int k){
if(y==1){
if(x<k)
return x;
while(x>=k){
int res=0;
while(x){
res+=x%k;
x/=k;
}
x=res;
}
return x;
}
if(y%2==1){
return root(root(x,y/2,k)*root(x,y/2,k)*root(x,1,k),1,k);
}
else return root(root(x,y/2,k)*root(x,y/2,k),1,k);
}
int main(){
int x,y,k;
while(cin>>x>>y>>k){
cout<<root(x,y,k)<<endl;
}
return 0;
}