[刷题笔记] Luogu P9345 夕阳西下几时回

发布时间 2023-08-16 22:32:39作者: SXqwq

Problem

Description

给定一个整数\(n\),有一个数组\(a\)的内容是\(1,2,3\)……\(n\)。(不一定按照顺序排列,只保证内容)特别地,我们令\(a_{n+1}=1\)

还有一个数组\(b\),满足\(b_i=gcd(a_i,a_{i+1})\)\(gcd(a,b)\)\(a\)\(b\)的最大公约数)

给定一个整数\(k\),试构造出一种排列使得\(b\)数组中不同的数字数量为\(k\)个。如果不存在直接输出No即可,无需输出排列。

Analysis

结论题。

首先一个结论,\(b\)数组中最多有\(⌊\frac{n}{2}⌋\)不同的数字

证明:由于\(\forall a_i \in[1,n]\)且内容不重复,\(a\)的大小为\(n\),因此对于\(\forall a_i,\forall a_j\)最大公约数最大为\(⌊\frac{n}{2}⌋\)。如果最大公约数超过\(⌊\frac{n}{2}⌋\)则一定不满足\(\forall a_i \in[1,n]\)。得证。

那么第一问就解决了,判断是否\(k\leq ⌊\frac{n}{2}⌋\)即可。

接下来如何构造呢?

我们先忽略有\(k\)个不同的数字,考虑如何最大化也就是有\(⌊\frac{n}{2}⌋\)个不同数字的时候如何构造。

由于\(gcd\)是取相邻的两数,我们肯定要尽可能的使一个数和她的倍数相邻。举个例子,如果\(n=6\)\(1,2,4,3,6\)这样的构造方式显然是最优的。

到这里我们已经有了一个初步的思路,那么如何去重呢?也就是构造的时候不能出现重复的数字。

这很简单,我们可以先跑一遍2的整次幂,然后再枚举奇数的倍数进行构造。显然奇数的倍数一定不是二的整次幂。这样我们就实现了去重。

那么从奇数开始不断\(\times 2\)有没有可能重复呢?

显然不会。因为一个数字(除了刚开始的奇数)一定是由上一个数字数字\(\times 2\)扩散来的,而同理上一个数字一定是由上一个数字\(\times 2\)得到的......容易发现一个数字可以被哪个奇数扩散到是唯一的。所以不会重复。证毕。

其实到这里我们就已经可以解决CF的问题了:CF1858C
和Luogu重题了被CN选手骂但还是rated了,回复No comment/cf


接下来考虑构造\(k\)个不同的数字。

构造\(k\)个不同的数字我们只需要操作到\(k\times 2\)就可以了。至于在区间\([k\times 2,n]\),直接相邻构造即可,因为相邻数字的\(gcd=1\),不会影响答案。

至此,本题得解,代码如下:

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int T;
int main()
{
	cin>>T;
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        if(k > n/2) 
        {
            cout<<"No"<<endl;
            continue;
        }
        cout<<"Yes"<<endl;
        cout<<"1 ";
        for(int i=n;i>=2*k+1;i--) cout<<i<<" "; //多余部分直接输出
        for(int i=2;i<=2*k;i*=2) cout<<i<<" "; //构造2的整次幂
        for(int i=3;i<=2*k;i+=2) //奇数构造
        {
            for(int j=i;j<=2*k;j*=2) cout<<j<<" ";
        }
        cout<<endl;
    }
	return 0;
}