ARC112E 题解

ARC112E 题解

前言

这是一道构造题,构造题最需要的就是自然的解题思路。

这篇文章将会给读者展现流畅的思路。

题面

原题传送门

题意

给定互不相同的数列 \(A\),问是否可以重排 \(A\) 使得 \(A\) 的前缀 LCM 单调递增。

思路

妙妙构造题。

引理 \(1\):如果第 \(i\) 个位置可以放的数的集合为 \(B\)\(B\) 内的数的先后顺序不影响答案。

证明:因为 \(A\) 每一个数都不同,那么对于这个集合 \(B\),里面每一个数都比前 \(i-1\) 个数的 LCM 都至少多一个不同的因子,所以其实先后顺序并不重要,怎么放都是合法的。

正片开始!

首先看到题,先考虑从前往后加数,但是却发现第 \(i\) 个数会对 \([i+1,n]\) 的数是什么产生影响,第 \(i\) 个数能放的集合 \(B\) 我们就不好选数,所以我们考虑正难则反,我们反着做,反着在已经填的数的开头加数。

我们现在开始观察题目,容易得出要是一个 \(A_i\) 满足 \(\operatorname{lcm}_{j\neq i}(A_j)<\operatorname{lcm}_{1\leqslant j\leqslant n}(A_j)\),那么 \(A_i\) 随便放在哪个位置都是合法的,因为它比所有的数都多一个独一无二的因子,而我们最好把它放在最后,这样不会要求其他的任何数要比它多一个独一无二的因子(这里注意 \(A\) 并不是要只所有的数,而可以只所有还没有加到数组中的数)。

我们考虑我们现在要加入 \(x\)(把 \(x\) 加入已经确定的开头,剩余的数的末尾),剩余 \(i\) 个数构成的集合 \(A\),那能加入 \(x\) 当且仅当 \(\gcd(x,\operatorname{lcm}_{y\in A}y)<x\),转化一下就变成 \(\operatorname{lcm}(\gcd_{y\in A}(x,y))<x\)[1](因为如果暴力取统计 \(\operatorname{lcm}_{y\in A}y\) 会爆 long long)。

由于引理 \(1\),所以我们只要找到一个可以加的数,那么我们就直接给它加进去即可,如果没有数可以加进去,那么就是无解。

这样我们就做完了,时间复杂度为 \(O(n^3\log V)\)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
// #pragma GCC optimize(2)
// #pragma GCC optimize(3)
// #define gc getchar
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#define FILE(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define FIN(s) freopen(s".in","r",stdin);
#define FOUT(s) freopen(s".out","w",stdout);
#define ll long long
#define re register int
#define rl register ll
#define il inline
#define yes putchar('Y'),putchar('e'),putchar('s'),putchar('\n')
#define no putchar('N'),putchar('o'),putchar('\n')
using namespace std;
const int MN=105;
ll n,a[MN],ans[MN];
bool vis[MN];
char buf[1<<23],*p1=buf,*p2=buf;
il void write(rl n){if(n<0){putchar('-');write(-n);return;}if(n>9)write(n/10);putchar(n%10+'0');}
il ll read(){ll x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}return x*f;}
il ll lcm(rl a, rl b){return a/__gcd(a,b)*b;}//先除防止爆long long
int main(){
    // ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    n=read();
    for(re i=1; i<=n; ++i) a[i]=read();
    for(re i=n; i; --i){
        ll p=0;
        for(re j=1; j<=n; ++j) if(!vis[j]){
            ll num=1;
            for(re k=1; k<=n; ++k) if(!vis[k]&&j!=k) num=lcm(num,__gcd(a[j],a[k]));
            if(num<a[j]){p=j;break;}
        }
        if(!p){no;return 0;}
        ans[i]=a[p];vis[p]=true;
    }yes;
    for(re i=1; i<=n; ++i) write(ans[i]),putchar(' ');putchar('\n');
    return 0;
}//250825

  1. 由唯一分解定理,两个数的 gcd 是对指数取 min,lcm 是对指数取 max,而 \(\min(\max\limits_y(x,y))=\max(\min\limits_y(x,y))\)(即 min 和 max 满足结合律),所以 lcm 和 gcd 也满足结合律,即这句话是正确的。 ↩︎

posted @ 2025-08-27 00:02  naroto2022  阅读(5)  评论(0)    收藏  举报