2020杭电多校2 1006 The Oculus

题目

在这里插入图片描述
题目链接

大致题意是这样的:
数字可以被唯一分解成为一系列斐波那契数的加和的形式。现在给定两个这样的数字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;
			}
		}
		
	}
	
}

猜你喜欢

转载自blog.csdn.net/wayne_lee_lwc/article/details/107553491