题目
大致题意是这样的:
数字可以被唯一分解成为一系列斐波那契数的加和的形式。现在给定两个这样的数字a
和数字b
将他们的乘积c
也表示成为斐波那契数的加和的形式。随后将c其中非首位的一个1改成0,题目要求哪一位是被修改的一位。
例如样例给定的情况:
a=4 b=5 a*b=20
20分解成为斐波那契数加和:20 = 2 + 5 + 13
表示成为数组就是:0 1 0 1 0 1,输入的数组为:0 1 0 0 0 1。因此被改变的位下标为4
暴力
首先分析一下,将某一位从1变成0,其实就是从原数中减去那一位的斐波那契数,由这点,就可以得出下面的式子,假设改变位的下标为k,则有:
A * B = C + Fk
倒腾整理一下:
Fk = A * B - C
其中ABC暴力的求出来就好了,复杂度是个O(N)(N为数组长度,ΣN <= 5000000)
使用暴力之前需要对斐波那契数列进行预处理,这个也暴力求就好了。主要问题就是要找一个精美的模数,使得几百万个斐波那契数在其下不重复出现。随便选个大质数是个不错的选择,但是万万没想到,2^64也是一个可以满足要求的模数。使用 2^64的好处在于,可以使用unsigned long long
直接进行运算而不用取模,让它自然溢出就好。(这波出题人闪到了俺的腰)。
答案也就直接在数列中比对查找即可,当然你也可以选择用map来实现,但是这对复杂度没有影响,瓶颈在暴力求ABC。
当然,相似的读入和求解过程不妨封装一下。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<fstream>
#define N 4000050
using namespace std;
typedef unsigned long long ull;
ull fibo[3 * N];
ull a,b,c,d;
ull partA[N],partB[N],partC[N];
int T,la,lb,lc;
int tot = 0;
ull calc(ull * p,int l){
ull ans = 0;
for(int i = 1;i <= l;i++){
ans += p[i] * fibo[i];
}
return ans;
}
int getPart(ull * p){
int l;
scanf("%d",&l);
for(int i = 1;i <= l;i++){
scanf("%lld",&p[i]);
}
return l;
}
int main(){
fibo[1] = 1;
fibo[2] = 2;
tot = 2;
for(cin >> T;T;T--){
la = getPart(partA);
lb = getPart(partB);
lc = getPart(partC);
int ma = max(max(la,lb),lc);
for(;tot <= ma;){
tot++;
fibo[tot] = fibo[tot - 1] + fibo[tot - 2];
}
a = calc(partA,la);
b = calc(partB,lb);
c = calc(partC,lc);
d = a * b - c;
for(int i = 1;i <= tot;i++){
if(d == fibo[i]){
cout << i << endl;
break;
}
}
}
}