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

本文深入解析了一道ACM竞赛中的复杂算法题目,通过巧妙的数据结构预处理和数学技巧,高效地解决了求和问题。文章详细介绍了如何使用计数数组和前缀和策略来分解并求解给定公式,展示了高中生惊人的算法理解和编程能力。

传送门:算式子

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

题意

给定n,m,\(1\le x\le 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数组)的数量

ibkt[i]储存的值
121 1
231 1 2
341 1 3 3
431 1 2
551 1 5 5 5
6511 2 3 3
721 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\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/shengwang/p/9844726.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值