cdcq

梦幻小鱼干

导航

【LibreOJ NOIP Round #1】数列递推

这要是ACM题又是一道自闭题。。。。。。

原题:

 

 

本题主要考察同学们大胆猜想不用证明的精神

把an表示为p*a0+q*a1的形式,手玩前几项发现不难证明a1系数永远大于a0且越来越大

所以只要这俩别全0,整个数列的走向一定是这样的:

波动->出现相邻同号->起飞

原因很显然,由于a1系数越来越大,所以当n趋向于无穷时a1的符号比a0占优势

而且系数是k的多项式,所以应该飞得很快

担心飞得不够快的可以暴力算各种边界情况,你会发现都会起飞

接下来的难点是将这个规律转化为AC程序,有两个难点

第一个难点是你不知道飞到什么程度才能够保证比前面的都大,我的做法是暴力算前3k个(被卡了,改成300个AC)

第二个难点是爆longlong

我的做法是在暴力的途中,如果发现a[i]>oo(一个很大的数),那么就让a[i]=oo(负数同理)

这样就可以先常规方法找最值点,然后特判一下,如果s[n]已起飞到平流层进入巡航模式,那么就改写最大或最小值点

此外还有很多细节,都标在代码里了,WA了2发才AC,如果是ACM题又要当自闭人力

 

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 using ll=long long int;
 5 //ll oo=(ll)1000000007*1000000007;
 6 ll oo=(ll)1000000007*200000;  //warning: k=5e3
 7 ll rd(){
 8     ll bwl=0,mk=1;
 9     char ch=getchar();
10     while((ch<'0' || ch>'9') && ch!='-')  ch=getchar();
11     if(ch=='-'){  mk=-1;  ch=getchar();}
12     while(ch>='0' && ch<='9'){  bwl=bwl*10+ch-'0';  ch=getchar();}
13     return bwl*mk;
14 }
15 int n,m;
16 ll s[310000],a[310000],k;
17 int main(){
18     n=rd();
19     for(int i=1;i<=n;++i)  s[i]=rd()+1;
20     m=rd();
21     while(m --> 0){
22         a[1]=rd(),a[2]=rd(),k=rd();
23         if(a[1]==0 && a[2]==0){  //warning
24             printf("%lld %lld\n",s[1]-1,s[1]-1);
25             continue;
26         }
27         /*  warning: a[1]<0 a[2]<0 a[1]<a[2]
28         if(a[1]<0 && a[2]<0){
29             printf("%lld %lld\n",s[1]-1,s[n]-1);
30             continue;
31         }
32         if(a[1]>0 && a[2]>0){
33             printf("%lld %lld\n",s[n]-1,s[1]-1);
34             continue;
35         }
36         */
37         for(int i=3;i<=300;++i){
38             a[i]=a[i-1]*k+a[i-2];
39             if(a[i]>oo)  a[i]=oo;
40             if(a[i]<-oo)  a[i]=-oo;
41         }
42         if(s[1]>300 || a[s[1]]==oo || a[s[1]]==-oo){  //warning
43             if(a[300]>0)  printf("%lld %lld\n",s[n]-1,s[1]-1);
44             else  printf("%lld %lld\n",s[1]-1,s[n]-1);
45         }
46         else{
47             ll mx=s[1],mn=s[1];
48             for(int i=1;i<=n && s[i]<=300;++i){
49                 if(a[s[i]]>a[mx])  mx=s[i];
50                 if(a[s[i]]<a[mn])  mn=s[i];
51             }
52             if(a[mx]==oo)  mx=s[n];
53             if(a[mn]==-oo)  mn=s[n];
54             printf("%lld %lld\n",mx-1,mn-1);
55         }
56     }
57     return 0;
58 }
View Code

 

posted on 2021-03-02 21:58  cdcq  阅读(32)  评论(0编辑  收藏