【数学分析+除法分块】Codeforces830C[Bamboo Partition]题解

来源:互联网 发布:2017年99公益日数据 编辑:程序博客网 时间:2024/05/16 05:58

题目概述

n 个竹子,每个竹子刚开始高度为 0 ,每过一天会长高 1 ,现在每过 d 天来检查一次,如果 i 竹子的高度 ai 则将该竹子高度砍为 ai 且该竹子不再生长,求砍掉高度不超过 kd 最大为多少。

解题报告

除法分块性质: ni 排序去重之后是 O(n) 的。

简单说明:一个数的因子是 O(n) 的。


先推式子喽:

i=1n{d[(ai1) mod d+1]}ki=1n[d(ai1dai1d+1)]kndi=1n(aidai1d)knd+di=1nai1dk+i=1nai

根据除法分块的性质, ai1d 只有 ai 种情况,而 n 个则有 namax 种情况,所以暴力枚举然后 O(n) 验证就可以了,效率 O(n2amax) ,好像有点慢……而且有较多的细节处理……


不妨继续推:

di=1nai+d1dk+i=1naii=1nai+d1dk+ni=1aidi=1nai+d1dk+ni=1aid

由于右边只有 k+ni=1ai 块,而每块中 d 越大,左边的式子越小(右边不变),所以每块中有价值的只有最大的 d ,那么还是暴力枚举即可。效率 O(nk+ni=1ai) ,比之前的要好一些,也没什么细节处理。

示例程序

我真的没有在水博客……

#include<cstdio>#include<algorithm>using namespace std;typedef long long LL;const int maxn=100;int n,a[maxn+5];LL K,ans;bool check(LL d) {LL sum=0;for (int i=1;i<=n;i++) sum+=(a[i]-1)/d+1;return sum<=K/d;}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    scanf("%d%lld",&n,&K);for (int i=1;i<=n;i++) scanf("%d",&a[i]),K+=a[i];    for (LL l=1,r;l<=K;l=r+1) if (check(r=K/(K/l))) ans=r;    return printf("%lld\n",ans),0;}