1. 程式人生 > >XSY原創題 斐波那契數列

XSY原創題 斐波那契數列

題目大意

給定長度為$n$序列$A$,將它劃分成儘可能少的若干部分,使得任意部分內兩兩之和均不為斐波那契數列中的某一項。

 

題解

不難發現$2\times 10^9$之內的斐波那契數不超過$50$個

先求出第$i$個數之前最後一個能和第$i$個數相加為斐波那契數的位置$last_i$。

考慮每一部分$[l,r]$只需滿足$\max\{last_i\}<l(i\in [l,r])$即可。

那麼設$F_i$表示以$i$為結尾最小化分數,那麼轉移到$i$的$j$顯然是一段左右端點均單調不遞減的區間,用單調佇列維護即可。

#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<" = "<<x
#define sp <<"  "
#define el <<endl
#define LL long long
#define M 100020
#define MAXN 2000000000
using namespace std;
int read(){
	int nm=0,fh=1; char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
map<int,int>MP; int n,m,p[M],F[M],last[M],G[M],q[M],hd,tl;
int main(){
	G[1]=1,G[2]=2,F[1]=1; n=read();
	for(m=2;(LL)G[m-1]+(LL)G[m]<=MAXN;m++) G[m+1]=G[m]+G[m-1];
	for(int i=1;i<=n;i++) p[i]=read();
	MP[p[1]]=1,q[tl++]=0,q[tl++]=1;
	for(int i=2,now=0;i<=n;i++){
		last[i]=0;
		for(int j=0;j<=m;j++){
			if(!MP.count(G[j]-p[i])) continue;
			int k=MP[G[j]-p[i]]; last[i]=max(last[i],k);
		} now=max(now,last[i]);
		while(q[hd]<now) hd++; F[i]=F[q[hd]]+1,MP[p[i]]=i;
		while(F[q[tl-1]]>=F[i]&&hd<tl) tl--; q[tl++]=i;
	}
	printf("%d\n",F[n]);
	return 0;
}