zoukankan      html  css  js  c++  java
  • What day is that day?(快速幂,打表找周期,或者求通项公式)

    有些题怎么都解不出来,这时候可以打表,找规律,求通项公式等,这些方法让人拍手叫绝,真不错……

    Description

    It's Saturday today, what day is it after 11 + 22 + 33 + ... + NN days?

    Input

    There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

    There is only one line containing one integer N (1 <= N <= 1000000000).

    Output

    For each test case, output one string indicating the day of week.

    Sample Input

    2
    1
    2
    

    Sample Output

    Sunday
    Thursday
    

    Hint

    A week consists of Sunday, Monday, Tuesday, Wednesday, Thursday, Friday and Saturday.

    source

    这道题比赛的时候没做出来,因为不知道周期是什么,虽然用的快速幂,算到10^6以前很快,再大就算的很慢,题目数据为10^9, 这样早超时了,

    后来看网上说打表可以找到周期294,我就把n = n % 294加到我的代码里就对了,55555~~~~~~当时我怎么不会打表,会打表的话就会ac了

    主要是打表和快速幂

    但是后来学长给讲了一个方法,求通项公式的方法,比打表高大上多了,其中可以边算边mod 7,但是这样的话用公式可能出现分数,因为除以一个数之后mod 7相当于乘以一个数的mod 7逆,分数问题就被解决啦,学长好厉害,下边把整个我写题的过程写出来

    刚才学了下打表,大概是这样,

    比如题目里有多组测试数据,第一个测试数据让你输出第1000个,那么你就可以把前1000个存起来,

    如果下一组测试数据让你输出第500个的时候你就不用重新计算,而是把前面算过的第500个输出就好,

    如果下一组测试让你输出第2000个,你就可以直接从1001开始算,前面算过的存储起来就不用再算了

    令:如果题目中数据非常大的时候比如说1000000000,无法一个一个正常计算的时候,你就可以找找题中的答案是不是有规律,结果是否存在周期性,

    可以通过打表把前面存的数据打出来找规律,比如试试前五百个数据间是否有规律

    可以打出500个,依次改变每行的数据个数,比较首行数据是否存在规律性

    #include <stdio.h>
    long long fun(int x, int n)
    {
        long pow = x, ret = 1;
        while(n)
        {
            if(n&1)
                ret = ret * pow % 7;
            pow *= pow % 7;
            n /= 2;
        }
        return ret;
    }
    int main()
    {
        long long n, ans, t;
        scanf("%lld", &t);
        while(t--)
        {
            ans = 0;
            scanf("%lld",&n);
            n = n%294;
            for(int i = 1; i <= n; i++)
            {
                ans += fun(i, i) % 7;
                ans %= 7;
            }
            switch(ans)
            {
                case 1: printf("Sunday
    ");break;
                case 2: printf("Monday
    ");break;
                case 3: printf("Tuesday
    ");break;
                case 4: printf("Wednesday
    ");break;
                case 5: printf("Thursday
    ");break;
                case 6: printf("Friday
    ");break;
                case 0: printf("Saturday
    ");break;
            }
        }
        return 0;
    }

     这道题我打表的代码是这样的,

    刚开始我不知道每行21个数据的时候每九行首行的数据就会出现周期性循环,

    我先看了前30个数没有规律,然后我试了500个数据的情况下打表

    从每行有15个数据开始试,试到每行21个的时候发现首行开始有规律,所以周期为21*9

    #include <stdio.h>
    long long fun(int x, int n)
    {
        long pow = x, ret = 1;
        while(n)
        {
            if(n&1)
                ret = ret * pow % 7;
            pow *= pow % 7;
            n /= 2;
        }
        return ret;
    }
    int main()
    {
        long long n, res, ans[1000], t;
        scanf("%lld", &t);
        while(t--)
        {
            res = 0;
            scanf("%lld",&n);
            for(int i = 1; i <= n; i++)
            {
                res += fun(i, i) % 7;
                res %= 7;
                ans[i] = res;
            }
            for(int i = 1; i <= n; i++)
            {
                if((i-1) % 21 == 0)
                    printf("
    ");
                switch(ans[i])
                {
                    case 1: printf("7 ");break;
                    case 2: printf("1 ");break;
                    case 3: printf("2 ");break;
                    case 4: printf("3 ");break;
                    case 5: printf("4 ");break;
                    case 6: printf("5 ");break;
                    case 0: printf("6 ");break;
                }
            }
            printf("
    
    ");
        }
        return 0;
    }

     3达标找周期的方法太笨了,还是学长给讲的求通项公式的方法好用,高端大气方便

    但是我写这东西的时候掉进一个陷阱里了,怎么都找不出错误,差点就放弃了,因为真的是让姐花了几个小时去找啊~~~~~~~~~~~~~

    求mod 7逆的时候要求逆的数必须是正数,如果是负数就一直加7直到这个数变成正数,然后求逆,………………坑啊,忘了这个,负数就直接求逆了,浪费好几个小时……

    这样写思路大概就是

    (1^1 + 2^2 + 3 ^ 3 + 4 ^4 + …… + n ^ n ) % 7 =

    1^1 + 2^2 + 3^3 + 4^4 + 5^5 + 6^6 + (7%7)^7 + 

    1^8 + 2^9 + 3^10 + 4^11 + 5 ^12 + 6^13 + 0^14+

    ……

    因为最终结果要mod 7所以每个数可以先mod 7然后再加起来所以就可以算通项公式

    1^1 + 1^8 + 1^15 + …… = n

    2^2 + 2^9 + 2^16 + …… = Qfact(2, 2) * (1 - Qfact(2, 7*n)) / mod7ni(1 - Qfact(2, 7))

    3^3 + 2^10 + 3^17+……= Qfact(3,3)*(1 - Qfact(3, 7*n)/ mod7ni(1 - Qfact(3, 7))

    ……

    以此类推,其中一定要注意负数的mod 7逆一定要把这个数变成正数要求逆,不然就悲剧了啊啊啊啊啊啊啊啊……

    #include <stdio.h>
    #define __int64 long long
    void exGcd(__int64 a, __int64 b, __int64 &x, __int64 &y)
    {
        if(b == 0)
        {
            x = 1;
            y = 0;
            return ;
        }
        else
        {
            exGcd(b, a % b, x, y);
            int t = x;
            x = y;
            y = t - a / b * y;
        }
    }
    __int64 Qfact(__int64 x, __int64 n)
    {
        __int64 ret = 1, pow = x;
        while(n)
        {
            if(n&1)
                ret = ret * pow % 7;
            pow = pow * pow % 7;
            n /= 2;
        }
        return ret;
    }
    __int64 mod7ni(__int64 n)
    {
        __int64 x, y;
        while(n < 0) n += 7;
        exGcd(n, 7, x, y);
        return (x% 7 - 7) % 7;
    }
    int main()
    {
        __int64 t, ans;
        scanf("%I64d", &t);
        while(t--)
        {
            __int64 n, tmp1, tmp2;
            scanf("%I64d", &n);
            tmp1 = n / 7;
            tmp2 = n % 7;
            ans = tmp1;
            if(tmp1)
            {
                for(int i = 2; i < 7; i++)
                {
                    ans = ans + Qfact(i, i) * (1 - Qfact(i, 7 * tmp1)) * mod7ni(1 - Qfact(i, 7)) % 7;
                    ans = (ans % 7 + 7) % 7;
                }
            }
            if(tmp2)
            {
                for(int i = 1; i <= tmp2; i++)
                {
                    ans = ans + Qfact(i, 7 * tmp1 + i) % 7;
                    ans = (ans % 7 + 7) % 7;
    
                }
            }
            ans = (ans % 7 + 7) % 7;
            switch(ans)
            {
                case  1: printf("Sunday
    ");break;
                case  2: printf("Monday
    ");break;
                case  3: printf("Tuesday
    ");break;
                case  4: printf("Wednesday
    ");break;
                case  5: printf("Thursday
    ");break;
                case  6: printf("Friday
    ");break;
                case  0: printf("Saturday
    ");break;
            }
        }
    
        return 0;
    }
  • 相关阅读:
    POJ 2752 KMP中next数组的理解
    KMP详解
    HDU 3221 矩阵快速幂+欧拉函数+降幂公式降幂
    POJ 3220 位运算+搜索
    反素数深度分析
    POJ 2886 线段树单点更新
    求反素数的方法
    CV第八课 GPU/CPU
    49. 字母异位词分组
    48. 旋转图像
  • 原文地址:https://www.cnblogs.com/rain-1/p/4761548.html
Copyright © 2011-2022 走看看