【NOIP练习赛】学习

【NOIP练习赛】T3、学习

Description

巨弱小 D 准备学习,有 n 份学习资料给他看,每份学习资料的 内容可以用一个正整数 ai 表示。小 D 如果在一天内学习了多份资料, 他只能记住这些资料的内容表示成的整数的最大公约数的部分。学习 若干份资料得到的收益是小 D 记下的内容之和,也就是学习的资料 数乘上这些资料内容的最大公约数。小 D 今天准备挑一段连续的资 料来学习,请你告诉他最大的收益是多少。 

Input

第一行一个正整数 n,表示资料数。 接下来 n 个正整数 ai,分别表示每份资料的内容。

对于 20%的数据,n<=100;

对于 40%的数据,n<=1000;

对于 70%的数据,n<=100000;

对于 100%的数据,n<=500000,ai<=${10^9}$

Output

输出一个整数,表示答案。 

Sample Input

5 30 60 20 20 20 

Sample Output

80 

Solution

应某人要求,来写题解

 

这道题还是有点难度,毕竟一个区间gcd,一直没想到解法,听dalao讲解后豁然开朗

 

确实本题只需边读边处理,在读入每个数时计算该数与前面所有数的gcd,又由gcd的一个小公式gcd(a,b,c)=gcd(gcd(a,b),c)所以我们只要用这个数和前面记录下的gcd再求出gcd就可以做到求区间[1..i],[2..i],[3..i]...,[i-1,i]的gcd

 

但是如果gcd重复太多,显然会T掉,而由于gcd(a,b,c)=gcd(gcd(a,b),c),则gcd(a,b)<=gcd(a,b,c)所以区间[1..i],[2..i],[3..i]...,[i-1,i]是单调不增的,那么我们就可以将gcd相同的数字合并,记录最左边的数(因为i为右端点,该点越左,gcd*区间长度就会越大)

 

那么为什么这样不会T呢?

 

因为我们这样处理的话,记录下的gcd都为a[i]的因数,而a[i]的因数不会超过${\log _2}a[i]$  个,所以我们记录下的前面的gcd个数不会超过${\log _2}a[i]$   个,只要暴力求,然后每次合并,并且每次更新最优解即可

 

还是挂程序吧

 

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define ll long long 
 4 using namespace std;
 5 int n,d[500050],k,l[500050];
 6 ll ans;
 7 int gcd(int a,int b){return a%b==0 ? b:gcd(b,a%b);}
 8 int main()
 9 {
10     scanf("%d",&n);
11     scanf("%d",&d[1]);
12     l[1]=1,k=1,ans=d[1];
13     for (int i=2;i<=n;i++) {
14         int m;
15         scanf("%d",&m);
16         for (int j=1;j<=k;j++) d[j]=gcd(d[j],m);
17         int po=1;
18         for (int j=2;j<=k;j++) if (d[po]!=d[j]) d[++po]=d[j],l[po]=l[j];
19         k=po;
20         if (d[k]!=m) d[++k]=m,l[k]=i;
21         for (int j=1;j<=k;j++) ans=max(ans,(ll)d[j]*(i-l[j]+1));
22     } 
23     printf("%lld",ans);
24     return 0;
25 }
View Code

 

posted @ 2017-10-26 20:53  Warm_Angel  阅读(250)  评论(2)    收藏  举报