题解:P11277 世界沉睡童话

Pro

构造序列 aa,长度为 nn,并使得序列有 kk 组倍数关系。

要求:ai2n1a_i\le 2n-1

Sol

我们最好让这个序列有序,因为这样只有后面的数对前面的数有倍数关系。

首先我们发现,n2n1n\sim 2n-1 中间是没有倍数关系的。

我们还发现,如果有 kk 个相同的数 aa,它对答案的贡献为 12k(k1)\dfrac{1}{2}k(k-1)

最后我们发现,在 kk11 的基础上,再添加一个 kk,会增加 nk+1n-k+1 组倍数关系。

我们发现特殊性质有一栏是这么写的:

kn1k\le n-1

这说明我们可以在 nkn\le k 时,将其缩小到范围 k<nk<n

这里使用性质 3,在前面疯狂添加 11,直到 k<nk<n

while(n<=k+1&&n!=0)
{
	cout<<"1 ";
	n--;
	k-=n;
}

如果此时 n=0n=0,可以直接 return 0; 了。

如果此时 n>0n>0,因为 k<nk<n,所以 2k<2n12k<2n-1,也就是说如果我们可以从前往后填 n,,n,n+1,n,\cdots,n,n+1,\cdots,从后往前填 2n1,2n2,2n-1,2n-2,\cdots,且不重复。

使用性质 2,一直枚举最大的 pp 使得 12p(p1)k\dfrac{1}{2}p(p-1)\le k,然后填上 ppaa,再使 aa11。初始 a=1a=1

最后 k=1k=1 的时候,从 2n12n-1 开始,从后往前填即可。

以上全部运用了性质 3。

时间复杂度是 O(n)O(n) 的。

void ans(int n,int k)
{
	while(n<=k+1&&n!=0)
	{
		cout<<"1 ";
		n--;
		k-=n;
	}
	if(n<=0) return;
	int a=n,lst=n;
	while(k&&lst)
	{
		int sum=sqrt(k)*2;
		while(sum*(sum-1)/2>k)sum--; // 这里应该可以直接用数学推出来
		k-=sum*(sum-1)/2;
		for(int i=1;i<=sum;i++) cout<<a<<' ';
		lst-=sum,a++;
	}
	for(int i=2*n-1;lst;i--) cout<<i<<' ',lst--;
}
posted @ 2024-11-15 09:27  sLMxf  阅读(21)  评论(0)    收藏  举报  来源