小苯的排列构造(最大公约数,构造,数学推导)

题目描述

格格有一个长度为 n n n 的排列 p p p,但她不记得 p p p 具体的样子,她只记得数组 a a a

其中: a i = g c d ( p 1 , p 2 , . . . , p i ) a_i=gcd(p_1,p_2,...,p_i) ai=gcd(p1,p2,...,pi),也就是说, a i a_i ai​ 表示排列 p p p 中前 i i i 个数字的最大公约数。

现在,她希望小苯将排列 p p p 复原出来,请你帮帮他吧。

(但有可能无解,这意味着格格给出的 a a a 数组可能是不正确的,此时输出 − 1 −1 1 即可。)

输入格式

输入包含两行。

第一行一个正整数 n ( 1 ≤ n ≤ 2 × 1 0 5 ) n (1≤n≤2×10^5) n(1n2×105),表示数组 a a a 的长度。

第二行 n n n 个正整数 a i ( 1 ≤ a i ≤ n ) a_i (1≤a_i≤n) ai(1ain),表示数组 a a a 的元素。

输出格式

输出包含一行 n n n 个正整数,表示符合条件的排列 p p p

如果有多个解,输出任意方案即可。

如果无解,请输出一个数字: − 1 −1 1

样例输入1

4
4 2 1 1

样例输出1

4 2 1 3

提交链接

https://ac.nowcoder.com/acm/contest/82957/D

提示

排列:长度为 n n n 的排列是一个数组,满足其中 1 1 1 n n n 的每个正整数恰好出现一次。

样例解释:

首先输出是一个排列,且满足格格的要求:

a 1 = g c d ( p 1 ) = 4 a_1=gcd(p_1)=4 a1=gcd(p1)=4

a 2 = g c d ( p 1 , p 2 ) = 2 a_2=gcd(p_1,p_2)=2 a2=gcd(p1,p2)=2

a 3 = g c d ( p 1 , p 2 , p 3 ) = 1 a_3=gcd(p_1,p_2,p_3)=1 a3=gcd(p1,p2,p3)=1

a 4 = g c d ( p 1 , p 2 , p 3 , p 4 ) = 1 a_4=gcd(p_1,p_2,p_3,p_4)=1 a4=gcd(p1,p2,p3,p4)=1

解析

根据 a [ i ] = g c d ( p [ 1 ] , . . . , p [ i ] ) a[i]=gcd(p[1],...,p[i]) a[i]=gcd(p[1],...,p[i]) a [ i − 1 ] = g c d ( p [ 1 ] , . . . , p [ i − 1 ] ) a[i-1]=gcd(p[1],...,p[i-1]) a[i1]=gcd(p[1],...,p[i1]),即 a [ i ] = g c d ( a [ i − 1 ] , p [ i ] ) a[i]=gcd(a[i-1],p[i]) a[i]=gcd(a[i1],p[i])

可以得到 a [ i − 1 ] % a [ i ] = 0 a[i-1]\%a[i]=0 a[i1]%a[i]=0

即, a a a 数组递减的,且满足 a [ i − 1 ] % a [ i ] = 0 a[i-1]\%a[i]=0 a[i1]%a[i]=0 a [ i − 1 ] = a [ i ] a[i-1]=a[i] a[i1]=a[i]

确定 a n s ans ans 数组,分为两种情况。

  1. a [ i − 1 ] > a [ i ] a[i-1]>a[i] a[i1]>a[i]
    a n s ans ans 数组的第 i i i 个位置填 a [ i ] a[i] a[i]

    证明:
    a [ i − 1 ] = g c d ( a n s [ 1 ] , . . . , a n s [ i − 1 ] ) > a [ i ] = g c d ( a n s [ 1 ] , . . . , a n s [ i ] ) a[i-1]=gcd(ans[1],...,ans[i-1])>a[i]=gcd(ans[1],...,ans[i]) a[i1]=gcd(ans[1],...,ans[i1])>a[i]=gcd(ans[1],...,ans[i]) = > => =>
    m i n ( a n s [ 1 ] , . . , a n s [ i − 1 ] ) > m i n ( a n s [ 1 ] , . . . , a n s [ 1 ] ) min(ans[1],..,ans[i-1])>min(ans[1],...,ans[1]) min(ans[1],..,ans[i1])>min(ans[1],...,ans[1]) = > => =>
    a n s [ 1 ] ∼ a n s [ i − 1 ] ans[1] \sim ans[i-1] ans[1]ans[i1] a n s [ i ] ans[i] ans[i] 不相等, a [ i ] a[i] a[i] 未使用。又要保证 a n s [ 1 ] ∼ a n s [ i ] ans[1] \sim ans[i] ans[1]ans[i] 的最大公约数为 a [ i ] a[i] a[i],则 a n s [ i ] = a [ i ] ans[i]=a[i] ans[i]=a[i]

  2. a [ i − 1 ] = a [ i ] a[i-1]=a[i] a[i1]=a[i]
    a n s [ i ] ans[i] ans[i] 数组的第 i i i 个位置填 a [ i ] a[i] a[i] 未使用的倍数。

a [ i ] = g c d ( a n s [ 1 ] , . . , a n s [ i ] ) a[i]=gcd(ans[1],..,ans[i]) a[i]=gcd(ans[1],..,ans[i]),可以得到 a n s [ i ] ans[i] ans[i] a [ i ] a[i] a[i] 的倍数, a n s [ i − 1 ] ans[i-1] ans[i1] a [ i ] a[i] a[i] 的倍数。

我们可以这样构造: a [ i ] a[i] a[i] 的倍数从小到大使用。即当填 a n s ans ans 数组的第 i i i 个位置时,小于等于 a n s [ i − 1 ] ans[i-1] ans[i1] a [ i ] a[i] a[i] 的倍数都已经被使用。
我们可以从 a n s [ i − 1 ] + a [ i ] ans[i-1]+a[i] ans[i1]+a[i] 开始找 a [ i ] a[i] a[i] 的倍数,大大减少时间复杂度。

参考代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 9;
int n, a[maxn], ans[maxn];
bool vis[maxn];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    bool book = false;
    for (int i = 1; i <= n; i++)
    {
        if (i >= 2 && a[i - 1] % a[i] != 0)  //a数组必须满足 a[i-1] % a[i] = 0
        {
            book = true;
            break;
        }
    }
    if (!book)
    {
        for (int i = 1; i <= n; i++)
        {
            if (a[i - 1] != a[i])
                ans[i] = a[i];
            else
            {
                for (int k = ans[i - 1] + a[i]; k <= n; k += a[i]) // 找a[i]的倍数
                {
                    if (!vis[k])
                    {
                        ans[i] = k;
                        break;
                    }
                }
            }
            if (!ans[i])
            {
                book = true;
                break;
            }
            vis[ans[i]] = true;
        }
    }
    if (book)
        cout << -1;
    else
    {
        for (int i = 1; i <= n; i++)
            cout << ans[i] << " ";
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值