【解题报告】BestCoder Round #80

题目链接

A.Lucky(HDU5665)

思路

题目问在题给数集的每个数都能使用无限次的情况下,能否通过令数集中某些数相加的方法得到全体自然数。根据百度百科的说法,自然数的定义有争议。在数学领域中,自然数从 1 开始,在计算机科学领域中,自然数从 0 开始。但是这里用的是后者的定义(我是看见很多人WA才猜到的,还好手速比较慢……)。于是判断数集中是否同时含有 0 1 两个元素即可。

代码

#include <cstdio>

bool zero, one;
int t, n, a;

int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        zero = one = false;
        for(int i = 0; i < n; i++) {
            scanf("%d", &a);
            if(a == 0) {
                zero = true;
            }
            if(a == 1) {
                one = true;
            }
        }
        puts(zero && one ? "YES" : "NO");
    }
    return 0;
}

B.Segment(HDU5666)

思路

先不考虑取模的情况。当 q=3 时,满足等式约数的 (x,y) (0,3),(1,2),(1,1),(2,1),(3,0),(0,0),(1,0),(0,1) ,而不在边界上的点只有 (1,1) 。同理当 q=4 时,满足条件的 (x,y) (1,1),(1,2),(2,1) 。不难发现,对任意的 x[1,q1] ,满足条件的 (x,y) (x,1),(x,2),...,(x,qx1) 。根据数列求和公式,有 ans=(q1)(q2)2 。由于 q 非常大,这里直接让 q1 q2 相乘是不行的,要么用边取模边相乘的办法,要么用Java大整数实现。注意,如果用边取边相乘的办法的话,除以 2 的操作要对 q1 q2 中的偶数做,然后再相乘。

代码

#include <cstdio>

typedef long long ll;
ll t, b, m, x, y, ans;

ll modMul(ll a, ll b, ll m) {
    ll ans = 0;
    for(; b > 0; b >>= 1) {
        if (b & 1) {
            ans = (ans + a) % m;
        }
        a = (a << 1) % m;
    }
    return ans;
}

int main() {
    scanf("%I64d", &t);
    while(t--) {
        scanf("%I64d%I64d", &b, &m);
        if(b <= 2) {
            puts("0");
            continue;
        }
        x = b - 1;
        y = b - 2;
        if(x % 2 == 0) {
            x /= 2;
        }
        if(y % 2 == 0) {
            y /= 2;
        }
        printf("%I64d\n", modMul(x, y, m));
    }
    return 0;
}

C.Sequence(HDU5667)

思路

由于题目直接给出了递推式,因此这是个递推的题目。根据题意可以直接写出前几项,结果发现(其实可以直接观察出来)数列 fn 的每一项都是以 a 为底的幂函数式。于是可以只研究它们的幂。为了方便设 gn=loga(fn) ,于是递推关系又可以写成

gn=0,1,cgn1+gn2+b,n=1n=2otherwise

n>2 的情况用矩阵来表示可以写成

gngn11=c10100b01×gn1gn21

然后就用矩阵快速幂便能够快速地求出 gn 了。但是遗憾的是 gn 非常大不能直接求,但是非常幸运,我们求出的 gn a 的幂,而 a 的结果要对 p 取模,正好 p 是质数。根据费马小定理

ap1=1(modp)

可以得到降幂公式

akmodp=akmod(p1)+(p1)modp

其中的 +p1 是用来防止 bmod(p1)=0 的。

综上所述,最终的答案为

agnmod(p1)+(p1)modp

代码

#include <iostream>
#include <cstring>
using namespace std;

typedef unsigned long long ll;
ll t, n, m, a, b, c, p;

// 矩阵结构体
struct matrix {
    int n, m;
    ll a[5][5];
    matrix(int n = 3, int m = 3): n(n), m(m) {
        memset(a, 0, sizeof(a));        
    }
    // 矩阵模乘
    matrix mod_mul(matrix &b, ll mod) const {
        matrix tmp(n, b.m);
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < b.m; j++) {
                for(int k = 0; k < m; k++) {
                    tmp.a[i][j] = (tmp.a[i][j] + a[i][k] * b.a[k][j] % mod) % mod;
                }                
            }            
        }
        return tmp;
    }
    // 矩阵取模快速幂
    matrix mod_pow(ll nn, ll mod) const {
        matrix a = *this, tmp(n, n);
        for(int i = 0; i < n; i++) {
            tmp.a[i][i] = 1;
        }
        for(; nn > 0; nn >>= 1) {
            if(nn & 1) {
                tmp = tmp.mod_mul(a, mod);
            }
            a = a.mod_mul(a, mod);
        }
        return tmp;
    }
};

// 快速幂函数
ll modPow(ll a, ll n, ll mod) {
    ll ans = 1;
    for(; n > 0; n >>= 1) {
        if(n & 1) ans = (ans * a) % mod;
        a = (a * a) % mod;
    }
    return ans;
};

int main() {
    matrix mat, res;
    mat.a[0][1] = mat.a[1][0] = mat.a[2][2] = 1;
    cin >> t;
    while(t--) {
        cin >> n >> a >> b >> c >> m;
        if(n == 1) {
            cout << 1 << endl;
            continue;
        }
        if(n == 2) {
            cout << modPow(a, b, m) << endl;
            continue;
        }
        // 填充矩阵
        mat.a[0][2] = b;
        mat.a[0][0] = c;
        // 矩阵快速幂求g(n)
        res = mat.mod_pow(n - 2, m - 1);
        // 费马小定理
        p = (b * res.a[0][0] + res.a[0][2]) % (m - 1) + m - 1;
        // 快速幂求结果
        cout << modPow(a, p, m) << endl;
    }
    return 0;
}

你可能感兴趣的:(解题报告,BestCoder,Round-#80)