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

    科技2024-08-18  78

    题目

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

    接下来会有 n n n个询问,每次询问对于一个数列,给出 a 0 , a 1 , k a_0,a_1,k a0,a1,k,递推式为 a i + 2 = k a i + 1 + a i a_{i+2}=ka_{i+1}+a_i ai+2=kai+1+ai max ⁡ x ∈ S a x \max_{x\in S} a_x maxxSax为多少。

    n ≤ 3 ∗ 1 0 5 n\le 3*10^5 n3105

    m ≤ 1 0 5 m\le 10^5 m105


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

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

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

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

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

    后面怎么做就显然了。


    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; }
    Processed: 0.009, SQL: 8