6818. 【2020.10.07提高组模拟】数列递推

题目

有个非负整数集合\(S\),大小为\(m\)

接下来会有\(n\)个询问,每次询问对于一个数列,给出\(a_0,a_1,k\),递推式为\(a_{i+2}=ka_{i+1}+a_i\)\(\max_{x\in S} a_x\)为多少。

\(n\le 3*10^5\)

\(m\le 10^5\)


比赛的时候直接推通项来搞,最终被它的精度问题搞死了。。。

假如存在相邻的两项不异号,那么后面将会是单调的。

尝试去找到第一个相邻两项不异号的,然后可以发现它的下标是\(O(\log_{k+1})\)级别的。

具体证明考虑相邻异号的序列最长有多长,可以发现为了维持它相邻异号,数列隔一个的值会缩小到至多\(\frac{1}{k+1}\)左右。

于是暴力找出这个位置,并且往后一直算到绝对值大于等于前两项的位置。

后面怎么做就显然了。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#define M 100005
#define ll long long
int m,s[M];
ll k;
ll a[M];
ll mx,mn,ansx,ansn;
void upd(ll v,ll id){
	if (ansx==-1 || v>mx || v==mx && id<ansx) ansx=id,mx=v;
	if (ansn==-1 || v<mn || v==mn && id<ansn) ansn=id,mn=v;
}
int main(){
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d",&m);
	for (int i=1;i<=m;++i)
		scanf("%d",&s[i]);
	int Q;
	scanf("%d",&Q);
	while (Q--){
		scanf("%lld%lld%lld",&a[0],&a[1],&k);
		int n=0;
		for (;a[n]*a[n+1]<0;++n)
			a[n+2]=a[n+1]*k+a[n];
		if (a[n]+a[n+1]>0){
			for (;a[n]<max(a[0],a[1]);++n)
				a[n+2]=a[n+1]*k+a[n];
		}
		else{
			for (;a[n]>min(a[0],a[1]);++n)
				a[n+2]=a[n+1]*k+a[n];
		}
		ansx=ansn=-1;
		int i=1;
		for (;i<=m && s[i]<=n+1;++i)
			upd(a[s[i]],s[i]);
		if (s[m]>n+1){
			if (a[n]+a[n+1]>0){
				if (ansn==-1) ansn=s[1];
				upd(LLONG_MAX,s[m]);
			}
			else if (a[n]+a[n+1]<0){
				if (ansx==-1) ansx=s[1];
				upd(LLONG_MIN,s[m]);
			}
			else
				upd(0,s[i]);
		}
		printf("%lld %lld\n",ansx,ansn);
	}
	return 0;
}
posted @ 2020-10-07 18:35  jz_597  阅读(109)  评论(0编辑  收藏  举报