国庆长乐酱油之旅day2

day2啦

130/300 小开心 qwq

T1:最大公约数

人性翻译:给定一串数字,把这串数字重新排列,使得这串数字的

第1个数字的公约数+第一个和第二个数的公约数+第一个数和第二个数和第三个数的最大公约数+…+整串数字的最大公约数之和

最大!!


首先拿到这道题, 我们可以知道每一次求出的公约数是单调不递增的,
且求这个公约数是可以递推实现的 
for example: 
假设这串数字的顺序为:2 4 9 5 5
设f[i]表示前i项的最大公约数
则可得
f[1]=2;
f[2]=gcd(f[1],4)=2;
f[3]=gcd(f[2],9)=1;
f[4]=gcd(f[3],5)=1;
f[5]=gcd(f[4],5)=1;
则ans=f[1]+f[2]+f[3]+f[4]+f[5]=7 

根据这个性质

我们就要让它求出下一个公约数的时候尽量让公约数不发生改变,
即如果原来之前所有项的公约数是5,那我们就要尽可能让这个5不发生改变,
因为这个公约数序列是单调不递增的,所以这样做才能达到决策最优

那么什么时候公约数才不会发生改变呢?

自然是当 目前选的这个数是之前的公约数的倍数时,更新后的公约数大小不变,即之前公约数是5,那如果现在让10跟在后面,那更新后的公约数任然是5。

上代码

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define size 100010

using namespace std;

int t,n,maxa;
long long ans;
long long num[size],used[size],sum[size];
//num[i]储存n个数中,是i的倍数的个数
//used[i]存储n个数中,数字i出现的次数
//sum[i]代表填完了所有i的倍数的数字后得到的最大值 

void start()
{
	freopen("gcd.in","r",stdin);
	freopen("gcd.out","w",stdout);
}

void end()
{
	fclose(stdin);
	fclose(stdout); 
} 

//求两数最大公约数 
int gcd(int a, int b)
{
    return b==0 ? a : gcd(b, a % b);
}

int main()
{
	start();
	
	scanf("%d",&t);
	while(t--)
	{
		maxa=0;
		ans=0;
		memset(num,0,sizeof(num));
		memset(used,0,sizeof(used));
		memset(sum,0,sizeof(sum));
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			int x;
			scanf("%d",&x);
			used[x]++;//记录每个数出现的次数 
			maxa=max(maxa,x);//找到出现的最大的数 
		}
		for(int i=maxa;i>=1;i--)//从出现的最大数往回推 
		{
			for(int j=i;j<=maxa;j+=i)//枚举i的倍数 
				num[i]+=used[j];//累计i的倍数的个数 
			sum[i]=num[i]*i;//初始化 
		}
		for(int i=1;i<=maxa;i++)
		//枚举先放的数 
		{
			for(int j=i;j<=maxa;j+=i)//枚举i的倍数
				sum[j]=max(sum[j],sum[i]+num[j]*(j-i));
				//sum[j]的值就等于sum[i]后面接上一段j的倍数的数字能得到的最大值 
			ans=max(ans,sum[i]);
			//处理最终答案 
		}
		printf("%lld\n",ans);
	}
	
	end();
	
	return 0;
}
//By Yfengzi 

T2:假的快速傅里叶变换

考场爆零,数论渣渣真的绝望

转化一下问题,我们令 g [ i ] g[i] g[i]表示满足 ( a × b ) ∣ i (a\times b) | i (a×b)i的有序正整数对 ( a , b ) (a ,b) (a,b)的个数。

∑ i = 1 n ∑ d ∣ i f [ d ] = ∑ i = 1 n g [ i ] \sum_{i=1}^n \sum_{d|i}f[d]= \sum_{i=1}^ng[i] i=1ndif[d]=i=1ng[i]

因为对于一个 i i i的约数 d d d,都有一个 i d \bf\frac{i}{d} di 的约数与之对应,并且 d = i d d=\frac{i}{d} d=di当时,正整数对只会计算一次。

思考一下 ∑ i = 1 n g [ i ] \sum_{i=1}^ng[i] i=1ng[i]的实际意义,即对 a b c &lt; n abc&lt;n abc<n满足的正整数 a , b , c a,b,c a,b,c所能组成所有有序数对的个数

考虑 a , b a,b a,b枚举得到 c c c的范围计算答案(就是这么暴力) ,直接枚举好像还是 O ( n 2 ) O(n^2) O(n2)

不妨限制下的 a , b , c a,b,c a,b,c大小关系,即令 a &lt; b &lt; c a&lt;b&lt;c a<b<c,最后乘上6,这样枚举的复杂度大概是 O ( n ) O(\sqrt{n}) O(n )。相等的情况单独枚举,复杂度 O ( n ) O(\sqrt{n}) O(n )

Code:

#include<iostream>
#include<cstdio>

using namespace std;

long long  n,ans;
int main()
{
	scanf("%lld",&n);
	for(long long a=1;a*a*a<=n;a++)//枚举a 
	{
		for(long long b=a+1;a*b*b<=n;b++)//枚举b 
		{
			long long c=n/b/a;//计算c 
			if(c>b) ans+=(c-b)*6;//如果符合满足a,b,c的大小关系,就计算 
		}
	}
	
	//处理a=b的情况 
	for(long long a=1;a*a<=n;a++)
	{
		long long c=n/a/a;//计算c 
		ans+=c*3;//*3,因为对于两个不同的数据,只有三种情况 
		if(c>=a) ans-=2;//处理a=b=c的情况,如果c>=a,则范围内,必有c=a,则只有一种情况 
	}
	
	printf("%lld",ans);
	
	return 0;
}

T3:Orz膜拜

由于本人太弱…到现在都只会打40的暴力

会了再更…

Soultion:

在这里插入图片描述

总结:

再也不敢说自己数论只会gcd了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值