CCF201709-5 除法_树状数组

201709-5 除法 传送门

这是一道描述相比于其他第五题来说比较简单的题目, 那么, 这道题真的简单吗?

答案是: 是的, 或者说想出正确解法不难.

了解这道题后会发现, 这不就是一道树状数组的题目么? 区间数的和, 某个数的更新, 很典型的树状数组. 确实是这样, 不过此题有几个坑.

首先是暴力解法, 按照题目的描述一步一步来即可, 当然的高分是不可能的, 只有30分. 不过这也启发我们, 即使你觉得第五题会做不出, 还是看一下的好, 没准就像这道题一样可以很轻松的拿到这30分.

然后用树状数组吧. 规规矩矩地写了个树状数组, 测试样例也通过了, 提交, 结果0分.原来是进行除法地时候忘记了改变num元素的值. 即num[j] /= w;这种小错误在没有反馈的情况下是很难自己找出来的, 所以让我冷汗直流.

改正错误之后再提交, 结果只有50分, 依然是TLE. 为什么? 整整10s的时间, 依然是超时了. 我就在思考, 是不是跟输入输出有关?

于是我尝试着关掉同步ios::sync_with_stdio(false), 尝试着全写成scanfprintf, 结果依然是这样.

于是我对照着别人的AC代码, 发现有这样一个优化, if (w == 1) continue;. 在这种情况下直接continue了, 不然由于程序的特殊性, 会浪费大量的时间, 而此题恰恰就卡了这个优化. 虽然不写这句对结果毫无影响.

改了后提交, 现在是80分了. 看来这一个优化值30分!

然后又是艰难地找不同, 发现了另一个可能的优化.

那就是if (w <= num[j] && num[j] % w == 0)中的w <= num[j]. 修改后提交, 100分了. 可是为什么呢? 我尝试将w <= num[j]改为num[j] != 0, 结果还是80分.

我的猜想是: 由于程序会进行大量的此条件的判断, 而判断取模的速度远比比较大小的速度慢(注意num[j] != 0的情况比w小而不为零的数还是会进行取模运算. 这道题也卡了这个优化. 而这个优化值20分, 非常关键的20分

还有要注意的一点是, 前面讨论的情况都是把int改成long long了的. 如果都是int, 只有30分! 所以注意程序不要溢出这个细节值整整70分!!!

还有要注意的是: long long的速度比int慢很多. 所以尽量不要把可以是int的数据弄成long long, 比如此题, 差距是两秒(7秒到5秒的差距).

还有合理的使用register, 也可以提升1-2s的时间效率. 大约20%-30%的效率提升吧.

这道题给我的启发是: 平时code的时候一定要养成关注细节优化的好习惯, 此题卡的几个细节都是平常很容易忽略的, 然而确实考试中拿高分的关键. 看似随意的细节, 如果我们去思考本质的效率, 可能会发现大有不同

满分代码, 不到50行.

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

const int maxn = 100000 + 5;
long long C[maxn] = {};
int n, m, num[maxn];

inline void update(register int pos, register int val)
{
    for (register long long i = pos; i <= n; i += (i & -i))
        C[i] += val;
}

inline long long getSum(register int x)
{
    register long long sum = 0;
    for (register int i = x; i >= 1; i -= (i & -i))
        sum += C[i];
    return sum;
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (register int i = 1; i <= n; ++i) {
        cin >> num[i];
        update(i, num[i]);
    }
    for (register int i = 1, ops, u, v, w; i <= m; ++i) {
        cin >> ops >> u >> v;
        if (ops == 1) {
            cin >> w;
            if (w == 1) continue;
            for (register int j = u; j <= v; ++j) {
                if (w <= num[j] && num[j] % w == 0) {
                    update(j, num[j] / w - num[j]);
                    num[j] /= w;
                }
            }
        } else {
            cout << getSum(v) - getSum(u - 1) << endl;
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值