蛮不错的一题,很有意思

并不是独创的算法,说是解释了其他人的代码也不为过,虽然自己也想到了同样的解法

0x00 特殊构造

首先考虑一下,每种幂次的数各一个

显然可以获得个数

0x01 直接加数的情况

考虑在上述数列中添加一些数,显然不管怎么添加幂次小于等于的数,其能获得的数仍然是从0开始的连续自然数,求数列和 + 1即为答案

0x02 数列不连续,出现“断层”

跳过数,直接加入数,显然现在能获得的数不再是连续的了,而是两段连续的数

一段从0开始,另一段从开始,长度均为,答案为

我们继续加入新数,假设成为以下情况

前面的个数,能将一段连续的自然数覆盖,

后面个数,当我们把数轴上长度为区间合并成一个点,并将坐标值除形成新数轴,在新数轴上也能覆盖连续的自然数区间

举个例子:

原数轴:

合并区间长度为4

原数轴:

新数轴: 分别对应原坐标:

例子结束,承接上文

对于新数轴,我们发现其中每一个点都对应原数轴一段以该点为起始的区间,

对于原数轴,我们每段长为的区间,能覆盖到的数只有个(比如这个区间和这个区间,都只覆盖了区间的前个数)

于是我们只要求得在新数轴上我们覆盖了多少个数,乘以之前求得的每段区间的数,就是答案,

显然又可以有新新数轴,总之可以递推下去,求得总答案

code:

#include<bits/stdc++.h>
using namespace std;

int n;
long long a[55];//十年OI一场空,不开long long 见祖宗

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i) scanf("%lld", a + i);
    sort(a + 1, a + n + 1);//排序,显然要从小到大分析,否则无法准确考虑连续
    a[n+1] = 1e18;
    long long tot = 0, ans = 1, cnt = 0, base = 1;//tot:用于判断断层,对应最后一段连续区间的右端点的值;cnt 当前状态数轴的覆盖点数;base:当前状态数轴单点对应原数轴区间长度
    for(int i = 1; i <= n + 1; ++ i){
       if(tot >= a[i]){//直接加
           tot += a[i];//右端点更新
           cnt += a[i]/base;//数轴上新增覆盖点数
       }else{//断层
           ans = ans * (cnt + 1);//计算区间大小为a[i]时,每段区间的答案
           tot = a[i]; cnt = 1; base = a[i];//更新右端点,区间大小,覆盖点数
       }
    }
    //准确来说,我们获得了区间大小为1e18,每段区间的答案
    printf("%lld", ans);
    return 0;
}