JZOJ 6818. 【2020.10.07提高组模拟】数列递推
题目大意
给出递推式
q
i
=
q
i
−
1
∗
K
+
q
i
−
2
q_i=q_{i-1}*K+q_{i-2}
qi=qi−1∗K+qi−2,其中
N
N
N次询问,每次
q
0
,
q
1
,
K
q_0,q_1,K
q0,q1,K给出,求以给定集合
S
S
S中的元素作为下表的
q
s
i
q_{s_i}
qsi的最大和最小值对应的
s
i
s_i
si,如有多个则最小化
s
i
s_i
si。
N
≤
3
∗
1
0
5
,
∣
S
∣
≤
1
0
5
N≤3*10^5,|S|≤10^5
N≤3∗105,∣S∣≤105
∣
q
0
∣
,
∣
q
1
∣
≤
1
0
7
,
1
≤
K
≤
5000
|q_0|,|q_1|≤10^7,1≤K≤5000
∣q0∣,∣q1∣≤107,1≤K≤5000
题解
有一个结论,当这个数列递推若干项后会出现全是同正/负的情况,那么接下来一直往后就只会递增/减,而且可以通过证明得出所谓“若干项”只有
l
o
g
log
log级别,那么前面的直接暴力递推,当出现同号时则直接再更新一次最大/小值,实现起来有些细节可能考虑不到,对着数据改一下就可以过了。细节:1、最后即便同正/负,到最后一项的值也未必大/小于前面的最大/小值,所以前面暴力递推的退出条件改为当前同正/负且当前值大/小于前面所有数的最大/小值;2、有可能一开始就是同号,所以不能只更新符号方向的极值,另一个极值也可能需要修改;3、可能出现
q
0
=
q
1
=
0
q_0=q_1=0
q0=q1=0,需要特判一下。以上是我个人遇到的问题和解决办法,因为写法不同可能有所差异。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
ll q[N];
int a[N];
int main() {
int n, m, i, j; ll k;
scanf("%d", &m);
for(i = 1; i <= m; i++) scanf("%d", &a[i]);
scanf("%d", &n);
for(i = 1; i <= n; i++) {
scanf("%lld%lld%lld", &q[0], &q[1], &k);
if(q[0] == q[1] && q[0] == 0) {
printf("%d %d\n", a[1], a[1]);
continue;
}
int mx = -1, mi = -1, l = 1;
ll Mx = max(q[0], q[1]), Mi = min(q[0], q[1]);
for(j = 2; j; j++) {
q[j] = q[j - 1] * k + q[j - 2];
if(q[j] > 0 && q[j - 1] > 0 && q[j] > Mx) break;
if(q[j] < 0 && q[j - 1] < 0 && q[j] < Mi) break;
Mx = max(Mx, q[j]), Mi = min(Mi, q[j]);
}
int t = j;
for(j = 1; j <= m && a[j] <= t; j++) {
if(mx == -1 || q[a[j]] > q[mx]) mx = a[j];
if(mi == -1 || q[a[j]] < q[mi]) mi = a[j];
}
if(a[m] > t) {
if(q[t] < 0) {
if(mx == -1) mx = a[1];
mi = a[m];
}
else {
if(mi == -1) mi = a[1];
mx = a[m];
}
}
printf("%d %d\n", mx, mi);
}
return 0;
}
转载请注明原文地址:https://blackberry.8miu.com/read-37316.html