Loading [MathJax]/jax/element/mml/optable/BasicLatin.js

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

题目

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

接下来会有n个询问,每次询问对于一个数列,给出a0,a1,k,递推式为ai+2=kai+1+aimax为多少。

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 @   jz_597  阅读(111)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示