zoukankan      html  css  js  c++  java
  • 830C

    分块+二分

    这道题思路很巧妙

    我们大概可以推出一个式子sigma(d-[(ai-1)%d+1])<=k,要求求出d的最大值

    然后我们化简一下,sigma(d-[(ai-1)-[(ai-1)/d]*d+1])<=k -> sigma(d-ai-[(ai-1)/d]*d)<=k

    直接枚举肯定炸,但是我们看见里面有一个下取整除法,我们想到了什么?莫比乌斯反演中的分块技巧!那么我们可以通过分块来减少枚举d的复杂度,然后在一定取值范围内二分就行了!

    然后,我们对于每个ai-1查找分块对应端点的最小值,也就是一段使得(ai-1)/d第一个变化的值,而其他值没有变化,也就是说我们对于每个ai枚举分块端点值后,每两个值相邻区间的值不会改变任何一个(ai-1)/d的值。

    然后每个ai有sqrt(ai)个值,那么一共就有n*sqrt(max(ai))的值,然后我们从大到小枚举每个值,如果一个值满足条件,那么我们需要二分找出满足答案的最大值,因为这个值只是在从这个值到下一个值-1这一段区间内任意(ai-1)/d不变,但是不一定满足,由于现在(ai-1)/d不变,那么上面那个式子就满足单调性了,于是就可以二分了。

    如果枚举的值范围过大,我们在看见除法的情况下可以用分块优化,可以大大降低复杂度,因为分块求出使一个值变化的最小的除数,这样我们就可以求出所有区间使得取这个区间内任意一个值所有数做除法的商不变

    最后push_back(j)是(ai-1)/d==0,其实也就是ai

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 110;
    int n;
    long long k, ans, sum, m;
    long long a[N];
    vector<long long> v;
    int main()
    {
        scanf("%d%I64d", &n, &k);
        for(int i = 1; i <= n; ++i) scanf("%I64d", &a[i]), sum += a[i], m = max(m, a[i]);
        long long tot = k + m;
        for(int i = 1; i <= n ; ++i)
        {
            long long j, t;
            for(j = 1, t = 0; j < a[i] && t < a[i]; j = t + 1)
                v.push_back(j), t = (a[i] - 1) / ((a[i] - 1) / j);
            v.push_back(j);    
        }
        for(int i = 0; i < v.size(); ++i) printf("%I64d ", v[i]);
        puts("");
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());
        v.push_back(100000000000000ll);
        for(int i = v.size() - 1; i >= 0; --i)
        {
            long long x = v[i];
            long long tot = 0;
            for(int j = 1; j <= n; ++j) tot += (a[j] - 1) / x;
            if(x * tot <= k + sum - (long long)n * x) 
            {
                long long l = x - 1, r = v[i + 1];
                while(r - l > 1ll)
                {
                    long long mid = (l + r) >> 1ll;
                    if(mid * tot <= k + sum - (long long)n * mid) l = ans = mid;
                    else r = mid;
                }    
                break; 
            }
        }        
        cout << ans << endl;
        return 0;
    }
    View Code
  • 相关阅读:
    Android布局尺寸思考
    正则表达式五分钟快速复习
    git gitignore文件失效处理
    华为手机Edittext光标(cursor)颜色修改
    AndroidStudio-OSX 常用快捷键整理
    OS X(EI Capitan)常用快捷键整理
    通过Foxit和坚果云实现iPad和PC的pdf同步阅读(修改,笔记)
    《程序员必读的职业规划书》职业生涯规划部分书摘及感想
    以神经网络使用为例的Matlab和Android混合编程
    写出优美代码的两个方式:一步到位VS迭代优化
  • 原文地址:https://www.cnblogs.com/19992147orz/p/7445417.html
Copyright © 2011-2022 走看看