URAL K-based Numbers(1-3)
题意:
内存限制:1000K
我们定义一个合法的K进制数为一个不含连续两个零的K进制数。
例如:
1010230 是一个合法的7位数。
1000198 不是合法的数。
0001234 不是7位数,是一个合法的4位数。
给你N,和K,M求出N位的K进制数的总数模M的值
这个是Version 3的翻译,其实建议直接做Version 3,当然,如果想感受一下这个好题的魅力,可以从第一题一直做到第三题,下面讲讲每个部分
Version 1
可以发现数据范围很小,这意味着答案并不会很大,考虑用递推的办法解决问题,设f[i]为k进制下i位的答案,那么开始寻求递推公式,注意这里很重要,后面两个题的解决都和这一个公式有着很大的关系。
我们先考虑当前这一位的决策,取0或不取0,不取0的情况是一定成立的,那么如果取0的话,i-1位一定不能取0,那么第i位取0的情况就只能搭配(f[i-2]*(k-1))这就是i-1位不为0的情况,然后直接推就好了
#include <cstdio>
#include <algorithm>
const int N = 20;
typedef long long LL;
#define rep(i, s, t) for(LL i = s; i <= t; ++i)
LL f[N];
int main() {
#ifndef ONLINE_JUDGE
freopen("input.in", "r", stdin);
freopen("res.out", "w", stdout);
#endif
LL n, k;
scanf("%lld%lld", &n, &k);
f[1] = k-1;
rep(i, 2, n)
f[i] = (f[i-1]+f[i-2])*(k-1);
printf("%lld\n", f[n]+f[n-1]);
return 0;
}
- Version 2
同样的,还是可以采用1的方法,但是发现会超LL(其实数据不会但是别水好吧。。)我们就直接一波高精度秒
#include <cstdio>
#include <cstring>
const int N = 2000 + 10;
const int Mod = 1e4;
#define max(a, b) a>b?a:b
#define rep(i, s, t) for(int i = s; i <= t; ++i)
#define dec(i, s, t) for(int i = s; i >= t; --i)
struct BN {
int x[N], len;
BN() {len = 0, memset(x, 0, sizeof x);}
void operator = (int d) {x[0] = d; len = 1;}
void update() {
rep(i, 0, len-1)
if(x[i] >= Mod) x[i+1] += x[i]/Mod, x[i] %= Mod;
while(x[len]) len++;
}
void print() {
printf("%d", x[len-1]);
dec(i, len-2, 0) printf("%.4d", x[i]);
puts("");
}
}Temp, f[N];
BN operator + (BN a, BN b) {
Temp.len = max(a.len, b.len);
rep(i, 0, Temp.len-1)
Temp.x[i] = a.x[i]+b.x[i];
Temp.update();
return Temp;
}
BN operator * (BN a, int d) {
rep(i, 0, a.len-1) a.x[i] *= d;
a.update(); return a;
}
int n, k;
int main() {
#ifndef ONLINE_JUDGE
freopen("input.in", "r", stdin);
freopen("res.out", "w", stdout);
#endif
scanf("%d%d", &n, &k);
f[0] = 1;
f[1] = k-1;
rep(i, 2, n)
f[i] = (f[i-1]+f[i-2])*(k-1);
f[n].print();
return 0;
}
调试是出现了不明未知错误,用比较黑暗的办法强行过的,当然不是打表。
- Version 3
好吧发现这个题给了模数,那当然好,但是发现n,k特别大,于是就只能矩阵快速幂,考虑用第一问的矩阵构造单位矩阵, 如下图
f[i-2],f[i-1] — 0 k-1 f[i-1],f[i]
f[i-1],f[i] — 1 k-1 f[i],f[i+1]
其实和斐波那契差不多,稍作改变就好;
有一个坑点,模数范围很大,需要使用快速加法,否则会爆LL,快速加法就是将乘法用类似快速幂的思想解决具体见代码
#include <cstdio>
#include <cstring>
typedef unsigned long long LL;
#define rep(i, s, t) for(int i = s; i <= t; ++i)
LL n, k, Mod, f[3];
struct Matrix {
LL x[2][2];
Matrix() {memset(x, 0, sizeof x);}
void pre() {
x[0][0] = 0;
x[1][0] = 1;
x[0][1] = x[1][1] = (k-1)%Mod;
}
}unit;
LL multy(LL a, LL b) {
LL Ans = 0;
for(; b; b >>= 1LL) {
if(b & 1) Ans = (Ans+a)%Mod;
a = (a+a) % Mod;
}
return Ans%Mod;
}
Matrix operator * (Matrix a, Matrix b) {
Matrix Ans;
rep(i, 0, 1)
rep(j, 0, 1)
rep(k, 0, 1)
Ans.x[i][j] = (Ans.x[i][j]+multy(a.x[i][k], b.x[k][j]))%Mod;
return Ans;
}
Matrix operator ^ (Matrix a, LL d) {
Matrix Ans = a;
for(--d; d; d >>= 1LL, a=a*a)
if(d & 1LL) Ans = Ans*a;
return Ans;
}
void solve() {
unit.pre();
unit = unit ^ (n-2);
unit.x[0][1] %= Mod;
unit.x[1][1] %= Mod;
printf("%llu\n",
(multy(f[1], unit.x[0][1])%Mod+multy(unit.x[1][1], f[2])%Mod)%Mod);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.in", "r", stdin);
freopen("res.out", "w", stdout);
#endif
while(~scanf("%llu%llu%llu", &n, &k, &Mod)) {
f[1] = (k-1)%Mod;
f[2] = multy(k, k-1)%Mod;
if(n == 2) printf("%llu\n", f[n]%Mod);
else solve();
}
return 0;
}