【牛客网】牛客练习赛19 F 算式子【数学--递推 、前缀、数字】

传送门:算式子

花了一些时间理解AC的代码,震惊,代码真的是短小精悍,推理能力很强亦或者是做题多,见的多。
能够理解里面的逻辑真的挺难的

题意

给定n,m,(1le xle m),求(sum_{i=1}^n{( ⌊frac {a_i} {x} ⌋ +⌊ frac {x} {a_i} ⌋ )})

分析

介绍一下用到的变量。
初始时

a数组存放值
cnt[i]表示i出现的次数
bkt[i]储存结果

求和分为两部分,分开求解。

part1: (sum_{i=1}^n{( ⌊frac {a_i} {x} ⌋ )})

首先预处理cnt数组,处理后 cnt[i] 表示a数组中 >= i的数字有多少个。
然后求他们的后缀和(很神奇!)

part2: (sum_{i=1}^n{( ⌊frac {x} {a_i} ⌋ )})

bkt[i+1]储存i能够整除元素(a数组)的数量

i bkt[i] 储存的值
1 2 1 1
2 3 1 1 2
3 4 1 1 3 3
4 3 1 1 2
5 5 1 1 5 5 5
6 5 11 2 3 3
7 2 1 1

求前缀和,具体为什么可以这样做(我也不知道啊,但是带进去算确实是对的)

Online AC Code

/*
参考:https://www.nowcoder.com/acm/contest/view-submission?submissionId=36414030
镇海中学 __asm
寥寥数行,其中蕴含了很强的推理知识,对前缀、数字有很好的掌握
佩服佩服,高中生牛逼
*/
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;
#define MAXN 5000010
typedef long long lnt;

int n, m;
lnt bkt[MAXN], cnt[MAXN], ans;
int main()
{
	scanf("%d%d", &n, &m);
	// cnt x的出现次数
	for (int i = 1, x; i <= n; i++) scanf("%d", &x), cnt[x]++;
	
	cout<<"cnt: "<<endl;
	for(int i=1;i<=m;i++) cout<<cnt[i]<<" "; cout<<endl;
	
	// 储存第二部分的结果??
	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j += i) bkt[j] += cnt[i];
		

	for(int i=1;i<=m;i++)
		cout<<bkt[i]<<" ";
	cout<<endl;
	
	// 前缀和?
	for (int i = 1; i <= m; i++) bkt[i] += bkt[i - 1];
	
	for(int i=1;i<=m;i++) cout<<bkt[i]<<" "; cout<<endl;

	// 预处理第一部分?
	// 处理后 cnt[i] 表示>= i的数字有多少个。
	for (int i = m; i; i--) cnt[i] += cnt[i + 1];
	
	cout<<"cnt: "<<endl;
	for(int i=1;i<=m;i++) cout<<cnt[i]<<" "; cout<<endl;

	// 计算第一部分的结果?
	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j += i)
			bkt[i] += cnt[j];
		
	for(int i=1;i<=m;i++) cout<<bkt[i]<<" "; cout<<endl;

	// 计算结果?
	for (int i = 1; i <= m; i++)
		ans ^= bkt[i];	
	

	printf("%lld
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/shengwang/p/9844726.html