1682 GG取数

来源:互联网 发布:电脑系统破坏软件 编辑:程序博客网 时间:2024/05/22 10:31

GG取数

时间限制:1000 ms  |  内存限制:65535 KB
描述

任给出正整数n和k(1<=n<=1,000,000 , 0<=k<=n),取数规则如下:
例如n=16,k=4
第一次取数 1 取数后的余数为16-1=15
第二次取数 2 取数后的余数为15-2=13
第三次取数 4 取数后的余数为13-4=9
第四次取数 8 取数后的余数为 9-8=1
当第五次取数时,因为余数是1,不够取,此时作如下处理
余数1+k=5,再从1开始取
第五次取数 1 取数后的余数为5-1=4
第六次取数 2 取数后的余数为4-2=2
由于第七次取数4,但余数为2,又得重新加k,2+4=6,再从1开始取
第七次取数 1 取数后的余数为 6-1=5
第八次取数 2 取数后的余数为 5-2=3
第九次取数 4 但不够取
3+4=7
第九次取数 1 取数后的余数为 7-1=6
第十次取数 2 取数后的余数为 6-2=4
第十一次取数 4 取数后的余数为 4-4=0 正好取完
由此可见,当n=16,k=4时,按上面方法11次取完

输入

输入共有t+1行,第一行是一个整数t(0<=t<=10000),表示共有t组数据。下面t行,每行有两个int类型的正整数,n和k。

输出

每组测试数据对应一行输出。如果正好取完,输出按照取数规则取完所需要的取数次数。如果永远不能取完时,输出”Bad Number!”。

样例输入
116 4
样例输出
11
提示

Math is the most powerful tool for coding!



解题说明:这道题需要知道其中的数学规律,难度还是可以的,请教了一个ACM大牛后知道了该怎么去做。

设fun(n)返回经过一轮计算后的余数,如果n=2^k-1,返回0,否则找到小于n的最大的2^a,返回n-2^a
对于给定的n,如果fun(n)=0,结束
否则设m=fun(n),k=1+2+4+……+a,a=fun(k),a+b=2^c
如果a=0,bad number
1. m>k+2b,m=fun(m+k)
2. m=0
3. m>=b,m-=b
4. m<b,m+=a
2、3、4应该是求解m=-bx+ay,用扩展欧几里德



补记:

今日去请教了刘庆辉老师,大致了解了王锐坚学长的思路,具体如下:

1、首先把n和k都看成二进制数,当n的位数超过k时,n总可以通过有限步降至和k位数相同的情况,假设剩下的数为m。设这里的步数为p

2、把k分解为1000..00(二进制)-a 其中被减数比k的位数高一位,a就是该数与k的差

3、m每次加上k可以看成是加上1000..00(二进制)再减去a

4、要是m能被取完则保证m=-ax+100..00(二进制)y 其中x,y存在正整数解

5、使用拓展欧几里德算法求解该方程,看看是否有解

6、如果存在解,最终的步数为x+p


#include <stdio.h>#include <string.h>int pow[21];int cnt;char mark[1000001];int func(int n) {    int i;    cnt = 0;    for (i = 0; i < 21; i++) {        if (n < pow[i]) {            return n;        }        cnt++;        n -= pow[i];    }    return n;}void solve() {    int n, k;    int i, j, a, b, m, res;    scanf("%d%d", &n, &k);    m = func(n);    if (m == 0) {        printf("%d\n", cnt);        return;    }    res = cnt;    for (i = n - m + 1; i >= 0; i--) {        if (mark[i] == 1) mark[i] = 0;    }    mark[m] = 1;        while (m > 0) {        m = func(m+k);        res += cnt;        if (mark[m] == 1) {            printf("Bad Number!\n");            return;        }        mark[m] = 1;    }    printf("%d\n", res);}int main() {    int i;    int t;        pow[0] = 1;    for (i = 1; i < 21; i++) {        pow[i] = 2 * pow[i-1];     }    scanf("%d", &t);    while (t--) {        solve();    }        return 0;}


原创粉丝点击