yueluoksguilu

每日一题——A - Max/Min AtCoder - abc356_e


1.题目大意:枚举两个数的Max/Min向下取整之和。
2.思路:一开始并没有想时间复杂度问题发现通过sort()排序来遍历每个最小值Min和后面最大值的和就是题目答案。你会发现仍然有问题,那就是取整的问题你就必须要优化然后发现很明显超时了。现在我们来换一个角度思考。搭配前缀和嘛。为什么要用前缀和呀,很明显在同一倍数里,ij~i(j+1)-1 整除都是j嘛。这样子的话可以用前缀和来快速找出这些数的个数呀。pre[i]代表i和之前出现的次数和,pre[i]-pre[i-1]代表i也就是Min出现的次数。枚举倍数j,pre[min(i(j+1)-1,1000000)]-pre[ij-1]来表示同一倍数Max的个数。ans+=(ll)j(pre[min(i(j+1)-1,1000000)]-pre[ij-1])cnt;记住每次不要忘记减去重复的数。

点击查看代码
#include<bits/stdc++.h>
#define ok ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define sd(a) cin>>a
#define sdd(a,b) cin>>a>>b
#define st(a) cout<<a<<endl
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll pre[N];
int main(){
	ok;
	int n;
	sd(n);
	fo(i,1,n){
		int x;
		sd(x);
		pre[x]++;
	}
	ll ans=0;
	fo(i,1,1e6)pre[i]=pre[i]+pre[i-1];
	fo(i,1,1e6){
		ll cnt=pre[i]-pre[i-1];
		if(!cnt)continue;
		for(int j=1;j*i<=1e6;j++){
			ans+=(ll)j*(pre[min(i*(j+1)-1,1000000)]-pre[i*j-1])*cnt;
		}
		ans-=(ll)cnt*(cnt+1)/2;
	}
	st(ans);
	return 0;
} 

posted on 2024-08-01 12:35  月落空山归路  阅读(10)  评论(0编辑  收藏  举报