Codeforces Round #323 (Div. 2) C gcd

本文介绍了一种算法,该算法可以根据给定的 GCD (最大公约数) 表格恢复原始数组。通过分析表格特性,采用排序及逐个元素验证的方法,确保了算法的有效性和正确性。



链接:戳这里


C. GCD Table
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
The GCD table G of size n × n for an array of positive integers a of length n is defined by formula

Let us remind you that the greatest common divisor (GCD) of two positive integers x and y is the greatest integer that is divisor of both x and y, it is denoted as . For example, for array a = {4, 3, 6, 2} of length 4 the GCD table will look as follows:

Given all the numbers of the GCD table G, restore array a.

Input
The first line contains number n (1 ≤ n ≤ 500) — the length of array a. The second line contains n2 space-separated numbers — the elements of the GCD table of G for array a.

All the numbers in the table are positive integers, not exceeding 109. Note that the elements are given in an arbitrary order. It is guaranteed that the set of the input data corresponds to some array a.

Output
In the single line print n positive integers — the elements of array a. If there are multiple possible solutions, you are allowed to print any of them.

Examples
input
4
2 1 2 3 4 3 2 6 1 1 2 2 1 2 3 2
output
4 3 6 2
input
1
42
output
42 
input
2
1 1 1 1
output
1 1 


题意:

给出n*n个数,去填一个n*n的表格,表格对应的gij=gcd(ai,aj)

要求你输出这n个数


思路:

分析一下,表格里面的数肯定是gij=gji的 这是一定的  ,然后ai=gii这也是一定的

那么我们从大到小排个序,当前大的数肯定是需要的,那么怎么统计他产生的数呢

因为新加入的数肯定要和之前加的数算gcd,那么在减去2,for过去就可以了


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#include<cmath>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define maxn 0x3f3f3f3f
#define MAX 1000100
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef unsigned long long ull;
#define INF (1ll<<60)-1
using namespace std;
int n;
map<int ,int > mp;
int anw[1000100];
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
int a[1000100];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n*n;i++){
        scanf("%d",&a[i]);
        mp[a[i]]++;
    }
    sort(a+1,a+n*n+1);
    int num=0;
    for(int i=n*n;i>=1;i--){
        if(mp[a[i]]){
            mp[a[i]]--;
            for(int j=1;j<=num;j++){
                mp[gcd(a[i],anw[j])]-=2;
            }
            anw[++num]=a[i];
        }
    }
    for(int i=1;i<=n;i++) cout<<anw[i]<<" ";
    puts("");
    return 0;
}
/*
3
2 2 2 2 1 1 1 1 3
*/


### 关于 Codeforces Round 997 Div. 2 的题目及解析 #### A. XOR Mixup 在这个问题中,给定了两个整数 \(a\) 和 \(b\) ,以及一个正整数 \(k\) 。目标是在不超过 \(k\) 步内通过交换 \(a\) 和 \(b\) 中任意一位来使得两者相等。如果可以在指定步数内完成,则返回 "YES";否则返回 "NO"[^1]。 对于这个问题的一个有效解决方案是计算不同位的数量并判断其是否小于等于两倍的 k 值加上 a 和 b 的二进制表示中最右边不同的位置索引之差。这是因为每一步最多能改变一对不匹配的位置状态。 ```cpp #include <bits/stdc++.h> using namespace std; int main() { int t; cin >> t; while(t--) { long long a, b, k; cin >> a >> b >> k; bitset<32> ba(a), bb(b); int diff = 0; for(int i=0; i<32; ++i){ if(ba[i]!=bb[i])++diff; } cout << ((abs(__builtin_ctzll(a ^ b)) + 2 * k >= diff) ? "YES\n":"NO\n"); } } ``` #### B. Array Shrinking 此题描述了一个数组缩小的过程:允许选取连续子数组并将它们替换为其最大公约数值(GCD),直到整个数组变成单个元素为止。询问最终剩余的那个唯一数字是什么样的最小可能值[^2]? 解决方法涉及到动态规划的思想——维护一个二维表 dp[][],其中dp\[l\]\[r\] 表达的是区间 \([l,r]\) 能够被压缩成的最大 GCD 数字。转移方程基于枚举中间点 m 来分割原区间为更小子区间的组合方式实现更新。 ```cpp const int N = 2e5+7; long long gcd(long long a,long long b){return !b?a:gcd(b,a%b);} vector<int> v(N); unordered_map<long long,int> mp[N]; void solve(){ int n; scanf("%d",&n); for(int i=1;i<=n;++i)v[i]=rand()%N+1; memset(mp,0,sizeof(mp)); for(int len=1;len<=n;++len) for(int l=1;l+len-1<=n;++l){ int r=l+len-1; if(len==1)mp[l][v[l]]=1; else{ unordered_set<long long> st; for(auto &p : mp[l]) if(p.second>=len-1&&gcd(v[r],p.first)==v[r]){ printf("0");exit(0); }else{st.insert(gcd(v[r],p.first));} for(auto x:st)mp[l][x]++; } } puts(to_string(mp[1].begin()->first).c_str()); } signed main(){solve();} ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值