首页 > 试题广场 >

求root(N, k)

[编程题]求root(N, k)
  • 热度指数:17990 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
    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)的值
示例1

输入

4 4 10

输出

4
// 网上抄来的,来源:http://www.acmerblog.com/jiudu-1085-2256.html

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include<iostream>

using namespace std;
typedef long long INT;

//计算幂取模a^b mod n, O(logb)

int modular_exponent(INT a,INT b,INT n){

 int ret=1;

 for (;b;b>>=1,a=a*a%n)

  if (b&1)

   ret=ret*a%n;

 return ret;

}


int main()

{

 int x, y, k;

 int i;

 int ret;

 

 while(scanf("%d%d%d", &x, &y, &k) == 3)

 {

  ret = modular_exponent(x, y, k-1);

  if(ret == 0)

   ret = k-1;

  printf("%d\n", ret);

 }

 return 0;

}
http://blog.csdn.net/merlini_/article/details/50651349
这题是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)的递归解法了。



[html] view plain copy
#include <iostream>
using namespace std;
int root(int x, int y, int k) {
if (y == 1) {
if (x < k)
return x;
else {
while (x >= k) {
int sum = 0;
while (x > 0) {
sum += x % k;
x /= k;
}
x = sum;
}
return x;
}
}
else {
int result = root(x, y / 2, k);
result *= result;
if (y % 2 == 1)
result *= root(x, 1, k);
return root(result, 1, k);
}
}
int main() {
int x, y, k;
while (cin >> x >> y >> k) {
cout << root(x, y, k) << endl;
}
return 0;
}
编辑于 2016-08-25 10:38:21 回复(5)
关键在于等式 root(x*y,k) = root(root(x,k)*root(y,k),k)
所以有如下例 root(x^111,k) = root(root(x^100,k)*root(x^10,k)*root(x,k),k)
               其中 root(x^100,k) = root(root(x^10)*root(x^10),k)
                       root(x^10,k) = root(root(x,k)*root(x,k),k)  
                    (次数为二进制表示)
过程类似二分求幂 只是穿上了root的外套 
#include <stdio.h>

int root(int N,int k)
{
    if(N >= k)
    {
        int num = 0;
        while(N != 0)
        {
            num += N%k;
            N /= k;
        }
        return root(num,k);
    }
    else
        return N;
}
void get(int x,int y,int k)  // root(x*y,k) = root(root(x,k)*root(y,k),k)
{
    int tmp = root(x,k);
    int rtn = 1;
    while(y>0)
    {
        if(y%2 == 1)
        {
            rtn = root(rtn*tmp,k);
        }
        tmp = root(tmp*tmp,k);
        y/=2;
    }
    printf("%d\n",rtn);
}
int main()
{
    int x,y,k;
    while(scanf("%d%d%d",&x,&y,&k) != EOF)
    {
        get(x,y,k);
    }
}

发表于 2018-03-08 10:48:34 回复(3)
(x^y)%k递归运算,直到x^y<k,可以使用快速模幂。
#include <stdio.h>
int main(){
    for(long x,y,k,r;~scanf("%ld%ld%ld",&x,&y,&k);){
        for(r=1,k-=1;y;r=(y&1?r*x%k:r),x=x*x%k,y>>=1);
        printf("%ld\n",r?r:k);
    }
    return 0;
}

发表于 2016-08-31 16:46:08 回复(9)
首先,
通过数学推导可知:
因为N>0,故
当N%(k-1)!=0时,root(N,k)=N%(k-1);
当N%(k-1)==0时,root(N,k)=k-1;
然后,
(x^y)%(k-1)=((x%(k-1))^y)%(k-1)
最后要知道快速幂,不然会超时。
#include <stdio.h>
int main()
{
    int x,y,k;
    while(scanf("%d%d%d",&x,&y,&k)!=EOF)
    {
        int z=1;
        while(y>=1)
        {
            x%=(k-1);
            if(y%2==1)
            {
                z*=x;
                z%=(k-1);
            }
            x*=x;
            y/=2;
        }
        if(z==0)
        {
            z=k-1;
        }
        printf("%d\n",z);
    }
    return 0;
}

编辑于 2020-03-01 16:42:06 回复(0)
1、模运算:(a * b)mod n = ((a mod n) b ) mod n
2、 N=a0+a1*k+a2*k^2+……+an*k^n;

N'=a0+a1+a2+……+an;

N-N'=a1*(k-1)+a2*(k^2-1)+……+an*(k^n-1);

提取(k-1)有: (N-N')%(K-1)=0;

继续递推下去有: (N-N')%(k-1) =0;

try:
    while True:
        x,y,k = list(map(int,input().split()))
        result = 1
        while y:
            if y&1:
                result = (result*x)%(k-1)
            x = (x*x)%(k-1)
            y>>=1
        result = result if result else k-1
        print(result)
except Exception:
    pass

http://blog.sina.com.cn/s/blog_8619a25801010wcy.html

编辑于 2018-10-10 15:31:56 回复(0)
举个例子root(4^7,k)=root(root(4^6,k)*root(4^1,k),k)
root(4^6,k)=root(root(4^4,k)*root(4^2,k),k)
因此是一种类似于二分求幂的思想,将y改成二进制7次就是111,然后知道一次就知道二次,知道二次就知道四次以此类推计科。
#include<stdio.h>
int Root(int x,int k){
    int sum;
    while(x>=k){
        sum=0;
        while(x>0){
            sum+=x%k;
            x=x/k;
        }
        x=sum;
    }
    return x;
}


int main(){
    int x,y,k;
    scanf("%d %d %d",&x,&y,&k);
    int rx=Root(x,k);
    int r=1;
    while(y>0){
        if(y%2!=0)
            r=Root(rx*r,k);
        rx=Root(rx*rx,k);
        y=y/2;
    }
    printf("%d",r);
}

发表于 2019-01-27 13:51:39 回复(2)
#include<stdio.h>
int mod;
long long FastPow(long long a,long long k){
    long long sum=1;
    while(k){
        if(k&1)sum=sum*a%mod;
        a=a*a%mod;
        k>>=1;
    }
    return sum;
}
int main(){
	long long x,y;
	while(scanf("%lld%lld%d",&x,&y,&mod)!=EOF){
        mod--;
		long long p=FastPow(x,y);
		printf("%lld\n",p==0?mod:p);
	}
}

发表于 2017-08-04 09:10:55 回复(0)

long long root(long long x,long long y,int k)
{
    long long ans = 1;
    while(y!=0)
    {
        if(y%2==1)
        ans = xans%k;
        y/=2;
        x = xx%k;
    }
        return ans;
}
int main()
{
    int x,y,k;
    cin>>x>>y>>k;
    int ans = root(x,y,k-1);
    if(ans == 0)
        ans = k-1;
    cout<<ans<<endl;
    return 0;
}

/*通过数学公式推导: Nr = N%(k-1)
由此得到最终的结果值为N%(k-1),当值为0时由于值域限制>0,结果就为k-1。
问题就转为求
N%(k-1)
这题考到了快速幂取模
首先第一个问题幂积的值(N)太大,无法保存下来,所以要分部取模。
这里的原理是积的模等于模的积。
要想分部就要将求幂的过程分部,这就用到了二分求幂:
int power(int a,int b)
{
    int ans=1;
    while(b!=0)
    {
        if(b%2==1)
        ans*=a;
        b/=2;
        a*=a;
    }
    return ans;
}
以上代码是求a的b次幂,原理是将b化为它的二进制形式,每位二进制对应不同的a的权重
例如:
0      1    0   0
a^8 a^4 a^2 a求a^4。
代码里b/=2是对二进制数进行右移,同时a*=a增大权值,当该位为1时(b%2==1)将相应的权值乘在ans上。这样就可以求出相应的幂值。
再根据乘积取模和取模后再乘积结果不变的原理,将取模加入到二分求幂的过程中:
ans = 1;
while(y!=0)
{
    if(y%2==1)
    ans = x*ans%k;//此处进行取模
    y/=2;
    x = x*x%k;//此处进行取模(只要是乘积的过程中的值,都可以加上取模)
}
return ans;
这样既加快了速度,由避免了数值太大溢出的问题。
不要忘记最后结果判断是否为0,为0时修改为k-1*/
编辑于 2019-03-02 17:39:07 回复(6)
<div> 类似于二分求幂。累死我了,这题不知道想了多久了…… </div> <pre class="prettyprint lang-cpp">#include <stdio.h> #include <math.h> #define N 50 int Root(int n, int k)//求root(n, k) {     int sum;     while(n>=k)     {         sum=0;         while(n>0)         {             sum+=n%k;             n/=k;         }         n=sum;     }     return n; } int Divide(int y, int bin[N])//把y拆成二进制数 {     int i=0;     while(y>0)     {         bin[i++]=y%2;         y/=2;     }     int pos=i-1;     for(; i<N; i++)     {         bin[i]=0;     }     return pos;//返回最高位位置 } int main() {     int x, y, k;     int bin[N];     while(scanf("%d %d %d", &x, &y, &k)!=EOF)     {         int rx=Root(x, k);         int r=1;         int pos=Divide(y, bin);         for(int i=pos; i>=0; i--)         {             r=Root(r*r, k);             if(bin[i]==1) r=Root(r*rx, k);         }         printf("%d\n", r);     }     return 0; }</pre> <br>
编辑于 2019-02-24 17:27:51 回复(6)
//我用两种方法求这个题。它们思想都是快速幂加上递推式。
//下面是迭代
#include<stdio.h>
typedef long long ll;
//迭代求x^y%k
ll N(ll x,ll y,ll k){
    ll ans=1;
    while(y){
        if(y&1)
            ans=(ans*x)%k;    
        x=(x*x)%k;
        y>>=1;
    }
    if(ans)//模不为0返回模
        return ans;
    else//模为0返回k
        return k;
}
int main(){
    ll x,y,k;
    while(scanf("%lld%lld%lld",&x,&y,&k)!=EOF){//循环输入
        printf("%lld",N(x,y,k-1));
    }
    return 0;
}
//下面是递归
#include<iostream>
using namespace std;
long N(long long x,long long y,long long k){
    long long ans=0;
    if(!y)
        return 1;
    else if(y&1)
        return x*N(x,y-1,k)%k;
    else{
        ans=N(x,y/2,k);
        return ans*ans%k;
    }
}
int main(){
    long long x,y,k,ans;
    while(cin>>x>>y>>k){
        ans=N(x,y,k-1);
        if(ans)
            cout<<ans;
        else
            cout<<k-1;
    }
    return 0;
}


发表于 2019-01-25 13:59:13 回复(1)
设N的k进制各位分别为a0,a1,a2,……,an,则有:
其中N^0=N,N^1=N',N^2=N''=(N')',……,N^r=(N^(r-1))'
设N^r<k,则有N^r=N%(k-1),且root(N,k)=root(N^r,k)=N^r=N%(k-1)
问题转化为求N%(k-1)=(x^y)%(k-1),直接运用快速模幂(快速幂+求模)方法,相关原理可参考书籍文献,此处不再赘述。
代码如下:
#include <iostream>
using namespace std;

long long root(long long x, long long y, int k) {
    long long ans = 1;
    while (y != 0) {
        if (y % 2 == 1)
            ans = x * ans % k;
        y /= 2;
        x = x * x % k;
    }
    return ans;
}

int main() {
    int x, y, k;
    cin >> x >> y >> k;
    int ans = root(x, y, k - 1);
    if (ans == 0)
        ans = k - 1;
    cout << ans << endl;
    return 0;
}


编辑于 2024-02-07 23:44:13 回复(0)
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Scanner in = new Scanner(System.in);
		while (in.hasNext()) {
			long aa = in.nextLong();
			long bb = in.nextLong();
			long kk = in.nextLong();
			//快速幂取模
			long ans = 1;
			kk--;
			while (bb > 0) {
				if (bb%2==1) {
					ans = (ans*aa)%kk;
					
				}
				bb/=2;
				aa=(aa*aa)%kk;
			}
			System.out.println(ans==0?kk:ans);
			
		}
	}

}

编辑于 2024-03-28 17:41:32 回复(0)
考察转化问题能力,需要一定的数学基础,并且注意分类讨论即可
#include <iostream>
using namespace std;
int root(long long a,long long b,int mod){
    int answer = 1;
    while(b>0){
        if(b % 2==1)
            answer=answer*a%mod;        
        b/=2;
        a=a*a%mod;
    }
    return answer>0?answer:mod;
}
int main() {  
    long long x,y;
    int k;
    while(cin>>x>>y>>k){
        cout<<root(x,y,k-1)<<endl;
    }
}

发表于 2024-03-25 19:48:05 回复(0)
root范围较小考虑同余性质。
发表于 2024-03-14 21:30:08 回复(0)
#include <iostream>
using namespace std;

long long FastPow(long long a, long long k, const int mod){
    if(k==0)
        return 1;
    if(k%2==1){
        return FastPow(a, k-1, mod) * a % mod;
    } else {
        long long ans = FastPow(a, k/2, mod);
        return ans * ans % mod;
    }
}

int main() {
    int x, y, k;
    while(cin>>x>>y>>k){
        k --;
        long long ans = FastPow(x, y, k);
        if(ans==0)
            cout<<k<<endl;
        else
            cout<<ans<<endl;
    }
    return 0;
}

发表于 2023-01-29 17:45:24 回复(0)
# include <iostream>
# include <algorithm>
# include <string>
# include <vector>
using namespace std;
int chengfang(int x, int y);
int sum_jinzhi(int m, int n);
int root(int N, int k);
int main()
{
    int x, y, k;
    while (cin >> x >> y >> k)
    {
        cout << root(chengfang(x, y), k) << endl;
    }
    return 0;
}

int chengfang(int x, int y)
{
    int sum = 1;
    for (int i = 0; i < y; i++)
    {
        sum = sum * x;
    }
    return sum;
}

int sum_jinzhi(int m, int n)//m代表数字,n代表进制
{
    vector<int> answer;
    while (m != 0)
    {
        answer.push_back(m % n);
        m /= n;
    }
    int sum=0;
    for (int i = 0; i < answer.size(); i++)
    {
        sum = sum + answer[i];
    }
    return sum;
}

int root(int N, int k)
{
    if (N < k)
    {
        return N;
    }
    else
    {
        root(sum_jinzhi(N,k), k);
    }
}

发表于 2022-03-18 20:15:05 回复(0)
对于N - N' mod k - 1 == 0的一点解释:
因为首先k-1 mod k-1 == 0这是显而易见
k * k - 1相当于在前者的基础上多了k * (k - 1),所以k * k - 1 mod k - 1 == 0
后面所有的都是在前面*k所以相当于(假设前一个是k^i)多了k^i * (k - 1),所以仍然是mod k - 1 == 0的
发表于 2021-06-08 21:28:12 回复(0)
#include <stdio.h>

//root(N, k) ,N' = N % (k -1 );如果 N % (k -1 ) = 0,则N' = k - 1;
int main(){
    int x, y, k;
    while(scanf("%d %d %d", &x, &y, &k) != EOF){
        int ans = 1;
        k = k - 1;
        //快速幂;  
        while(y){
            if(y % 2 == 1){
                ans  = (ans % k) * (x % k);
                ans %= k;//(a * b) % p = (a % p * b % p) % p 
                }
                y = y >> 1;
                x = (x % k ) * (x % k); 
                x %= k; 
        }
        if(ans == 0){
            ans = k;
        }
        printf("%d", ans);
    }
    return 0;
}

发表于 2021-03-21 19:56:53 回复(0)
Java版
import java.util.Scanner;

public class Main{
    
    public static void main(String[] args){
        
        Scanner in = new Scanner(System.in);
        
        while(in.hasNext()){
            long x = in.nextLong();
            long y = in.nextLong();
            long k = in.nextLong();
            
            long res = root(x, y, k);
            
            System.out.println(res);
        }
    }
    
    //快速幂算法
    public static long root(long x, long y, long k){
        
        long res = 1;
        
        while(y > 0){
            if(y % 2 == 1){
                res = res * x % (k - 1);
            }
        
            x = x * x % (k - 1);
            y /= 2;
        }
        
        return res == 0 ? (k - 1) : res;
    }
}



发表于 2021-03-03 22:56:44 回复(0)
#include<iostream>
#include<cstdio>
using namespace std;
long long root(long long x,long long y,int k)
{
    long long ans = 1;
    while(y!=0)
    {
        if(y%2==1)
        ans = x*ans%k;
        y/=2;
        x = x*x%k;
    }
        return ans;
}
int main()
{
    int x,y,k;
    cin>>x>>y>>k;
    int ans = root(x,y,k-1);
    if(ans == 0)
        ans = k-1;
    cout<<ans<<endl;
    return 0;
}

发表于 2021-01-27 17:52:21 回复(0)

问题信息

难度:
42条回答 13889浏览

热门推荐

通过挑战的用户

查看代码