求斐波那契数列第 ( a ^ b ) 项模 N 的值。
斐波那契数列在模 N 的情况下会有循环节,也就是说当出现连续两项分别等于 F[ 0 ],F[ 1 ] 的时候我们就找到了循环节 T,所以循环节 T 一定在 N ^ 2 的长度内出现。因为 N 比较小,就可以先求出 F[ 1 ] ... F[ N ^ 2 ],然后答案就是 F[ ( a ^ b ) % T ]。
#include
#include
using namespace std;
typedef unsigned long long ULL;
ULL a, b;
int n, f[1000010];
inline ULL read()
{
ULL ret = 0; char c = getchar();
while (!(c >= '0' && c <= '9')) c = getchar();
while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();
return ret;
}
void init()
{
a = read(); b = read();
scanf("%d", &n);
}
ULL mul(ULL a, ULL b, int n)
{
ULL ret = 0;
while (b){
if (b & 1) ret = (ret + a) % n;
a = (2 * a) % n;
b >>= 1;
}
return ret;
}
int power(ULL a, ULL b, int n)
{
ULL ret = 1;
a %= n;
while (b){
if (b & 1) ret = mul(ret, a, n);
a = mul(a, a, n);
b >>= 1;
}
return (int)ret;
}
void doit()
{
int n2 = n * n, r = 2;
f[0] = 0 % n; f[1] = 1 % n;
for (int i = 2; i <= n2; i ++){
f[i] = (f[i - 1] + f[i - 2]) % n;
if (f[i] == f[1] && f[i - 1] == f[0]){
r = i - 1;
break;
}
}
int pos = power(a, b, r);
printf("%d\n", f[pos] % n);
}
int main()
{
int T; scanf("%d", &T);
while (T --){
init();
doit();
}
return 0;
}
已知递推式 Xi = (a * Xi-1 + b) % 10001,并告诉你 X1,X3, X5 ... X2*t-1,请你求出 X2,X4,X6 ... X2*t。
因为递推式模了 10001,所以 a 的取值范围是 [ 0, 10000 ],这样就可以枚举 a。已知了 a ,通过递推式可以推出 X1 和 X3 之间的公式:X3 = ((a ^ 2) * X1 + a * b + b) % 10001,所以 ( a + 1 ) * b % 10001 = (X3 - ( a ^ 2 ) * X1) % 10001。这样就可以用扩展欧几里德求出 b,然后计算 Xi,看是否与已知的 X1,X3 ... X2*t-1 矛盾,如果矛盾则说明 a 不合法,否则就输出出来。
#include
using namespace std;
const int mod = 10001;
typedef long long LL;
int T;
LL s[10005];
void init()
{
for (int i = 1; i <= T; i ++)
scanf("%lld", &s[(i << 1) - 1]);
}
void ex_gcd(LL a, LL b, LL &d, LL &x, LL &y)
{
if (!b){
x = 1; y = 0; d = a;
return;
} else {
ex_gcd(b, a % b, d, x, y);
int t = x;
x = y; y = t - a / b * x;
}
}
void doit()
{
s[2] = s[1];
if(T >= 2){
for (int a = 0; a <= 10000; a ++){
LL A = a + 1, B = mod, C = s[3] - a * a * s[1];
LL x, y, d;
ex_gcd(A, B, d, x, y);
if (C % d == 0){
x = x * (C / d);
int i;
for (i = 1; i < T; i ++){
s[i << 1] = (s[(i << 1) - 1] * a + x) % mod;
if (s[(i << 1) + 1] != ((s[i << 1] * a + x) % mod)) break;
}
//printf("%d %lld %lld\n", a, x, s[(T << 1) - 1]);
s[T << 1] = (s[(T << 1) - 1] * a + x) % mod;
if (i == T) break;
}
}
}
for (int i = 1; i <= T; i ++) printf("%lld\n", s[i << 1]);
}
int main()
{
while (scanf("%d", &T) != EOF){
init();
doit();
}
return 0;
}
求组合数 C(p, q) / C(r, s)。
很有趣的题目,还是自己思考比较好,提示一下:唯一分解定理。
#include
#include
#include
using namespace std;
const int MAX_N = 10005;
int pri[1500], tot = 0;
bool check[MAX_N];
int p, q, r, s, cnt[1500];
inline void prime()
{
memset(check, 0, sizeof(check));
check[1] = 1;
for (int i = 2; i <= 10000; i ++){
if (!check[i]) pri[++ tot] = i;
for (int j = 1; j <= tot; j ++){
if (i * pri[j] > 10000) break;
check[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
}
}
}
void gao(int x, int d)
{
for (int i = 1; i <= tot; i ++){
while (x % pri[i] == 0){
x /= pri[i];
cnt[i] += d;
}
if (x == 1) break;
}
}
void calc(int n, int d)
{
for (int i = 1; i <= n; i ++)
gao(i, d);
}
void doit()
{
memset(cnt, 0, sizeof(cnt));
calc(p, 1); calc(q, -1); calc(p - q, -1);
calc(s, 1); calc(r - s, 1); calc(r, -1);
double ans = 1.0;
for (int i = 1; i <= tot; i ++)
ans *= pow(pri[i], cnt[i]);
printf("%.5f\n", ans);
}
int main()
{
prime();
while (scanf("%d%d%d%d", &p, &q, &r, &s) != EOF){
doit();
}
return 0;
}
输入一个整数 N,求至少两个整数,使得他们的最小公倍数为 N,且这些整数的和最小。输出最小的和。
利用唯一分解定理把 N 分解为 (a1 ^ p1) * (a2 ^ p2) * ... ,不难发现每个 (ai ^ pi) 作为一个单独的整数时最优。但是还有一些陷阱需要处理:N = 1 时答案为 1 + 1 = 2;N 只有一种因子时需要加个 1,还要注意 N = 2 ^ 31 - 1 是不要溢出。
#include
#include
#include
#include
using namespace std;
const int MAX_N = 1000005;
typedef long long LL;
int p[MAX_N], num[MAX_N], cnt = 0;
LL n;
void doit()
{
cnt = 0;
memset(p, 0, sizeof(p)); memset(num, 0, sizeof(num));
if (n == 1) { printf("2\n"); return; }
if (n == 2147483647) { printf("2147483648\n"); return; }
LL t = n;
for (int i = 2; i * i <= n; i ++){
if (t % i == 0){
p[++ cnt] = i;
while (t % i == 0){
t /= i;
num[cnt] ++;
}
}
if (t == 1) break;
}
if (t != 1) p[++ cnt] = t, num[cnt] ++;
if (cnt == 1) {
LL ans = pow(p[1], num[1]) + 1; printf("%lld\n", ans);
}
else {
LL ans = 0;
for (int i = 1; i <= cnt; i ++){
ans += pow(p[i], num[i]);
}
printf("%lld\n", ans);
}
}
int main()
{
int tot = 0;
while (cin >> n){
if (n == 0) break;
printf("Case %d: ", ++ tot);
doit();
}
return 0;
}