"蔚来杯"2022牛客暑期多校训练营6部分题题解

Array

卡了许久的题。果然我还是对于某些猜结论的题有些不太会。
不过比赛的时候还是被队友搞过了,队友NB。
这个题怎么说呢,感觉突破口还是在1/2上,为什么它要给1/a[i]的和<=1/2?
这个还可以先撇开一边,当我们手动去摸索规律的时候,发现每隔a[i]可以放一个i可以保证放的最少,但每个a[i]各不相同,我想放的时候已经被占据了,这个时候我们就想能不能把a[i]给规范一下,这样的话,每隔a[i]个放就可以变得有规律起来了。
接下来就是这个题最NB的地方,我们对a[i]进行缩放,注意我们缩放的时候,只能将a[i]减小,并且还要a[i]之间互成倍数(方便我们每隔a[i]个放i)。这个时候我们可以想想2进制,最多减小两倍,(因为是1/2的缘故)。那我们找<a[i]最大的2次幂。去替换a[i],可以发现,这样的话,a[i]最多变为原来的1/2(实际上不到)。所以这个时候1/a[i]<=1.
此时我们取最大的a[i]作为c的长度,将a[i]排序,尝试去做,在a[1]的,我们直接将长度m按照a[1]分组,每组第一个放对应下标即可。那么接下的a[2]呢?
只有两种情况,(1)a[2]=a[1],这个时候,发现a[2]的分组和a[1]的分组一样,并且在每组的情况一摸一样。这样我们也是直接在每一组相同位置放a[2]对应下标即可
(2)a[2]>a[1],又因为a[2],a[1]又都是2的整数幂,所以a[2]一定是a[1]的倍数,所以可以肯定的是a[2]分组后,每一组的情况也一摸一样,我们同样在每一组的一个相同空位置放上去即可。
通过这两种情况的讨论,通过简单的数学归纳可以发现,只要我们从小到大依次去放每个a[i]的时候,我们对当前序列对a[i]分组,每一组的情况必定一摸一样。这样我们就可以找一个每一组都空的位置放上去即可。
总的来说,构造题缩放还是第一次见,算是长见识了吧。缩放的大小可以根据限制<=1/2,也可以根据做题的时候的实际需求。(例如这个题我们在摸索规律的时候,就希望他们a[i]如果规范点的话该多好。)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,c[N];
pair<int,int>a[N];
set<int>s;
inline int get(int x)
{
	int t=1;
	while(t<x) t<<=1;
	return t>>1;
}
int main()
{
//	freopen("1.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;++i) 
	{
		int x;scanf("%d",&x);
		a[i]={get(x),i};
	}	
	sort(a+1,a+n+1);
	m=a[n].first;
	for(int i=1;i<=m;++i) s.insert(i);
	for(int i=1;i<=n;++i)
	{
		for(int j=(*s.begin());j<=m;j+=a[i].first) //从第一个空位开始,每隔a[i]就放,直接放即可,因为不同组的空位一定一致. 
		{
			c[j]=a[i].second;
			s.erase(j);
		}
	}
	printf("%d\n",m);
	for(int i=1;i<=m;++i) printf("%d ",c[i]==0?1:c[i]); 
	return  0;
}
posted @ 2022-08-07 10:00  逆天峰  阅读(45)  评论(2编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//