题目:
样例输入:
1
2
样例输出:
2
1 1
题意:给定一个长度为n的数组,满足∑ai^(-1)≤1/2。求长度为m的序列c0,c1,……,cm-1。其中以c序列作为循环节可以生成一个无限长的b数组,其中bi=c(i%m)。b数组必须要满足任意连续ai个数中要有一个值为i的数。
分析:先来大概看一下1/ai的含义是什么,ai代表连续ai个数中至少有一个i,那么1/ai也就是说平均一部分中要有1/ai的数是i,那么我们大概就能想到只要∑ai^(-1)≤1,我们就有可能构造出来一个满足题意的序列,而题目中给的条件是小于等于1/2,为了构造的方便性,我们不妨对构造要求进行加强,题目中要求每连续ai个数至少有一个i,而我们按照每ti个数就有一个i来进行构造,其中ti是小于等于ai的最大的二的幂次,这样构造显然是符合题意的,但是这样放缩后会不会使得原来能构造出来的序列现在构造不出来了呢?其实不会,由于ti是小于等于ai的最大的2的幂次,也就是说有2/ai>=1/ti>=1/ai,那么就有<=
<=1,那么我们也是可以构造成功的。而我们的m取值应该是什么呢?由于所有的ti都是一个2的幂次,那我们不妨让m取值为所有ti的最小公倍数,其实也就是最大的ti。我们按照ti从小到大开始构造,每次构造都从第一个空位开始,每隔ti个数就加入一个对应的数,这时候有人可能会问,如果我从第一个空位开始填数,如果到下一次填数时发现已经被填上其他的数了,那我不就不能构造了吗?其实这种情况是一定不会发生的,因为之前的tj是小于ti的,而且由于都是2的幂次,所以ti是tj的倍数,既然有一个空位,那么每隔ti个数依旧是空位,这是显然的,要不然一开始的位置绝对不可能是空位,倒着推也能推出来,所以按照ti从小到大的顺序是一定没有问题的。这时候有些人可能还有一个疑问,那会不会当填j时前aj个位置已经填满了呢?其实这种情况也是不会发生的,原因就在于假如我们不考虑所有的大于等于aj的数,那么就相当于我们在一个长度小于aj的区域填一些ai,其中ai小于aj,那么跟上面一样显然是有合理的构造序列满足题意,而且由于去掉了大于等于aj的一些数,所以倒数和是严格小于1的,那么就一定会有空位,而不可能出现填满的情况,所以就不会出现上面说的一开始的空位就不满足题意的情况,所以按照上面那种从小到大填的方法是一定没有问题的。
细节见代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e6+10;
struct node{
int val,id;
}p[N];
bool cmp(node a,node b)
{
return a.val<b.val;
}
int ans[N];
int main()
{
int n;
cin>>n;
int m=0;
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
p[i].id=i;p[i].val=1;
while(t>1)//找小于等于t的最大的2的幂
{
t>>=1;
p[i].val<<=1;
}
m=max(p[i].val,m);
}
sort(p+1,p+n+1,cmp);
printf("%d\n",m);
for(int i=1,j=1;i<=n;i++)
{
while(ans[j]&&j<=m) j++;//找第一个没填过的位置
for(int k=j;k<=m;k+=p[i].val)
ans[k]=p[i].id;
}
for(int i=1;i<=m;i++)//补上空位
if(!ans[i]) ans[i]=1;
for(int i=1;i<=m;i++)
printf("%d ",ans[i]);
return 0;
}