[CSP-S模拟测试]:D(暴力+剪枝)

题目传送门(内部题47)


输入格式

第一行一个正整数$n$。
第二行$n$个正整数,表示序列$A_i$。


输出格式

一行一个正整数,表示答案。


样例

样例输入:

5
30 60 20 20 20

样例输出:

80


数据范围与提示

样例解释:

最后四个元素形成的子序列权值最大。

数据范围:

对于前$30\%$的数据:$n\leqslant 2,000$
对于所有数据:
$1\leqslant n\leqslant {10}^5$
$1\leqslant A_i\leqslant {10}^9$


题解

再一次没有打正解……

$0\%$算法:

暴力枚举区间显然是$\Theta(n^2)$的,所以我们考虑$QJ$测试点,不得不承认这道题的数据不好造,所以我们可以当当前扫到的$a>gcd$时跳出即可。

毫无正确性,就是想吐槽一下出题人的数据。

不过说了也没用,真正考场上谁敢打呢?

时间复杂度:$\Theta($玄学$)$。

期望得分:$0$分。

实际得分:$100$分。

$100\%$算法:

暴力都会,我们现在考虑剪枝。

  $\alpha.$将所有数求一个$gcd$,然后都把它们除这个$gcd$,最后答案记得乘上就好了。

  $\beta.$如果以当前的$gcd$进行到序列最后也不能更新答案,那么$break$。

你可能会觉得会被卡成$\Theta(n^2)$,但是仔细分析一下会发现,最劣的情况即为等比数列,而等比数列长度不会长于$、\log \max(A_i)$,所以我们最多会将整个数列扫$\log \max(A_i)$遍。

时间复杂度:$\Theta(n\log \max(A_i)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

$0\%$算法:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100001];
long long ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		int gcd=a[i];
		for(int j=i+1;j<=n;j++)
		{
			gcd=__gcd(gcd,a[j]);
			if(1LL*(n-i+1)*gcd<=ans||gcd<a[j])break;
			if(gcd==1){ans=n-i+1;break;}
			ans=max(ans,1LL*(j-i+1)*gcd);
		}
	}
	printf("%lld",ans);
	return 0;
}

$100\%$算法:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100001];
int GCD;
long long ans;
int main()
{
	scanf("%d%d",&n,&a[1]);GCD=a[1];
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&a[i]);
		GCD=__gcd(GCD,a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		a[i]/=GCD;
		ans=max(ans,1LL*a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		int gcd=a[i];
		for(int j=i+1;j<=n;j++)
		{
			gcd=__gcd(gcd,a[j]);
			if(1LL*(n-i+1)*gcd<=ans)break;
			ans=max(ans,1LL*(j-i+1)*gcd);
		}
	}
	printf("%lld",ans*GCD);
	return 0;
}
 

rp++

posted @ 2019-09-19 18:19  HEOI-动动  阅读(270)  评论(0编辑  收藏  举报