题目链接
题目问在题给数集的每个数都能使用无限次的情况下,能否通过令数集中某些数相加的方法得到全体自然数。根据百度百科的说法,自然数的定义有争议。在数学领域中,自然数从 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;
}
先不考虑取模的情况。当 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,q−1] ,满足条件的 (x,y) 为 (x,1),(x,2),...,(x,q−x−1) 。根据数列求和公式,有 ans=(q−1)∗(q−2)2 。由于 q 非常大,这里直接让 q−1 和 q−2 相乘是不行的,要么用边取模边相乘的办法,要么用Java大整数实现。注意,如果用边取边相乘的办法的话,除以 2 的操作要对 q−1 和 q−2 中的偶数做,然后再相乘。
#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;
}
由于题目直接给出了递推式,因此这是个递推的题目。根据题意可以直接写出前几项,结果发现(其实可以直接观察出来)数列 fn 的每一项都是以 a 为底的幂函数式。于是可以只研究它们的幂。为了方便设 gn=loga(fn) ,于是递推关系又可以写成
gn=⎧⎩⎨0,1,cgn−1+gn−2+b,n=1n=2otherwise
将 n>2 的情况用矩阵来表示可以写成
⎛⎝⎜gngn−11⎞⎠⎟=⎛⎝⎜c10100b01⎞⎠⎟×⎛⎝⎜gn−1gn−21⎞⎠⎟
然后就用矩阵快速幂便能够快速地求出 gn 了。但是遗憾的是 gn 非常大不能直接求,但是非常幸运,我们求出的 gn 是 a 的幂,而 a 的结果要对 p 取模,正好 p 是质数。根据费马小定理
ap−1=1(modp)
可以得到降幂公式
akmodp=akmod(p−1)+(p−1)modp
其中的 +(p−1) 是用来防止 bmod(p−1)=0 的。
综上所述,最终的答案为
agnmod(p−1)+(p−1)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;
}