Codeforces 354C Vasya and Beautiful Arrays[dp+暴力]

本文探讨了在给定整数集合的情况下,通过减去指定范围内的任意整数,求解能够达到的最大公约数(GCD)的问题。通过分析最小元素和剪刀操作的概念,本文揭示了GCD的上下限,并提出了一种验证可行性的方法,该方法涉及枚举和前缀和技巧,以确定最大可行GCD。
摘要由CSDN通过智能技术生成

题意:

给出n个整数,对每个整数可以减去0-k的任意一个数

求这样操作后,n个数的最大GCD是多少


分析:

我们首先可以知道n个整数中最小的数是多少

而且,最终的答案肯定不大于这个数

这个n个整数中最小的数是答案的上限

然后对于答案的下限

可以肯定的是

1肯定是答案的下限

2呢?3呢?为什么1一定是

其实,0-k+1,都可以作为答案

为什么?

可以把k想象成一个剪刀

对k+1来说,任何数都可以剪掉0-k变成k+1的倍数(任何数模k+1的结果都是0-k)

所以0-k也可以,综上0-k+1都可以,所以答案的下限是k+1

答案处于[k+1,minn]

如果minn<k+1,那么就是minn

如果minn>k+1,则我们枚举这其中所有的数

看最大的能使得所有数列中的数经过操作之后GCD(all)确实能等于这个数的数是多少

也就是验证这个数的可行性,找出可行的数中最大的那个


怎么验证呢?给出一个数m,怎么才能知道可行不可行呢?

一步步来推理

每个数模m,结果都是0--m-1

对于0--m-1,我们的剪刀最多只能剪掉k

所以如果数列中某个数模m的结果大于k,则m不可行

那么我们是不是能这样做

对数列中的每一个数都模m呢

估计一下复杂度,枚举是100W,最多有30万个数,则100w*30w,T!


转化一下这个问题

我们是对每个数尝试一下它是不是处于可行区间

反过来,如果可行区间有这个数的话,是一样的

如果所有可行区间中存在的数的和是n,则这个m是可行的

可行区间是什么?

一个数模m即是x =m*d+x%m

x%m小于等于k,也就是x处于[m*d,m*d+k]

那么所有的可行区间就是

[m*1,m*1+k],[m*2,m*2+k],[m*3,m*3+k],[m*4,m*4+k]...

有logn个这样的区间

算下复杂度,n*logn

这些区间内出现的数的总和我们可以累加前缀和之差得到

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int NN=555555;
int vistmp[NN*2];
int vis[NN*2];
int f[NN];
int n,k;
int maxn;
bool check(int s){
	int sum=0;
	for(int i=1;i*s<=maxn;i++){
		sum += vis[min(maxn,i*s+k)] - vis[i*s-1];//注意细节
		if(s==7){
		}
	}
	return sum==n;
}
int main(){
    #ifndef ONLINE_JUDGE
        freopen("G:/in.txt","r",stdin);
        //freopen("G:/myout.txt","w",stdout);
    #endif
	cin>>n>>k;
	int minn=1<<30;
	for(int i=1;i<=n;i++){
		cin>>f[i];
		minn=min(minn,f[i]);
		maxn=max(maxn,f[i]);
		vistmp[f[i]]++;
	}
	vis[minn]=vistmp[minn];
	for(int i=minn+1;i<=maxn;i++)
        vis[i]=vis[i-1]+vistmp[i];
	if(minn<=k+1){
		cout<<minn<<endl;
		return 0;
	}
	for(int i=minn;i>=k+1;i--){
		if(check(i)){
			cout<<i<<endl;
			return 0;
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值