[2016湘潭邀请赛 A. 2016] 大数取模+循环节

[2016湘潭邀请赛 A. 2016] 大数取模+循环节

1. 题目链接

XTU OnlineJudge : [2016湘潭邀请赛 A. 2016]

2. 题意描述

【图片看不清可以放大。】
原题描述
给定一个 22 的矩阵 A 和一个大整数n,求 An 。矩阵每个元素对 7 取模数。1n<10100000,0Aij<7
Note: 题目故意强调了 2016 在这个题目里面的重要性。提示要选手往这个方面去想【叉姐真的已经是很良心了】。

3. 解题思路

看起来这个是一到矩阵快速幂的题目。但是,指数巨大。但是模数很小,而且题目提示了找循环节,显然的可以去考虑找循环节【不会找也没有关系,其实叉姐提示了循环节周期就是2016…】。
22 的矩阵,最多的情况有 74 中情况。那么将这 74 个矩阵都求出他们的循环周期。然后会发现,周期的最小公倍数是 336 , 所以先对大数 N 336, 然后,矩阵乘法就可以了。
2016=3366 , 【这就是为什么叉姐提示2016的原因了】。

4. 实现代码

/**
 * 找循环节代码
 */
PII getInfo() {
    vector<Mat> buf;
    vector<int> used(MOD * MOD * MOD * MOD, -1);
    B = A;

    int val, offset, T;
    for(int i = 0; ; i++) {
        val = B.H();
        if(used[val] != -1) {
            T = i - used[val];
            offset = used[val];
            break;
        }
        used[val] = i;
        buf.push_back(B);
        B = A * B;
    }
    return make_pair(offset, T);
}

void presolve() {
    int lcm = 1;
    for(int i = 0; i < MOD; i ++) {
        A.a[0][0] = i;
        for(int j = 0; j < MOD; j++) {
            A.a[0][1] = j;
            for(int k = 0; k < MOD; k++) {
                A.a[1][0] = k;
                for(int t = 0; t < MOD; t++) {
                    A.a[1][1] = t;
                    PII ret = getInfo();
                    lcm = lcm * ret.second / __gcd(lcm, ret.second);
                }
            }
        }
    }
    cout << lcm << endl;
}
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int MX = 2;
const int MOD = 7;

struct Mat {
    int a[MX][MX];
    void I() { memset(a, 0, sizeof(a)); }
    void E() {
        I();
        for(int i = 0; i < MX; i++) a[i][i] = 1;
    }
    int H() {
        int ret = 0;
        for(int i = 0; i < MX; i++) {
            for(int j = 0; j < MX; j++) {
                ret *= MOD;
                ret += a[i][j];
            }
        }
        return ret;
    }
    Mat operator * (const Mat& e) const {
        Mat ret; ret.I();
        for(int k = 0; k < MX; k++) {
            for(int i = 0; i < MX; i++) {
                if(a[i][k] == 0) continue;
                for(int j = 0; j < MX; j++) {
                    ret.a[i][j] += a[i][k] * e.a[k][j];
                    ret.a[i][j] %= MOD;
                }
            }
        }
        return ret;
    }
    Mat operator ^ (int b) const {
        Mat x(*this), ret; ret.E();
        while(b > 0) {
            if(b & 1) ret = ret * x;
            x = x * x;
            b >>= 1;
        }
        return ret;
    }
} A, B;

const int ML = 100000 + 4;
char s[ML];
/**
 * 大整数取模模板。
 */
int mod(char str[], int num) {
    int remainder = 0;
    for(int i = 0; str[i]; i++) {
        remainder = ((LL)remainder * 10 + str[i] - '0') % num;
    }
    return remainder;
}

int main() {
    while(~scanf("%s", s)) {
        int N  = mod(s, 336);
        scanf("%d %d", &A.a[0][0], &A.a[0][1]);
        scanf("%d %d", &A.a[1][0], &A.a[1][1]);
        A = A ^ N;
        printf("%d %d\n", A.a[0][0], A.a[0][1]);
        printf("%d %d\n", A.a[1][0], A.a[1][1]);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值