一
1
D-小苯的排列构造_牛客小白月赛94 (nowcoder.com)
题目描述
格格有一个长度为 n nn 的排列 p pp,但她不记得 p pp 具体的样子,她只记得数组 a aa。
其中:a i = g c d ( p 1 , p 2 , . . . , p i ) a_i = gcd(p_1, p_2,...,p_i)a
i
=gcd(p
1
,p
2
,...,p
i
),也就是说,a i a_ia
i
表示排列 p pp 中前 i ii 个数字的最大公约数。
现在,她希望小苯将排列 p pp 复原出来,请你帮帮他吧。
(但有可能无解,这意味着格格给出的 a aa 数组可能是不正确的,此时输出 − 1 -1−1 即可。)
输入描述
输入包含两行。
第一行一个正整数 n ( 1 ≤ n ≤ 2 × 1 0 5 ) n\ (1 \leq n \leq 2 \times 10^5)n (1≤n≤2×10
5
),表示数组 a aa 的长度。
第二行 n nn 个正整数 a i ( 1 ≤ a i ≤ n ) a_i\ (1 \leq a_i \leq n)a
i
(1≤a
i
≤n),表示数组 a aa 的元素。
输出描述
输出包含一行 n nn 个正整数,表示符合条件的排列 p pp。
如果有多个解,输出任意方案即可。
如果无解,请输出一个数字:− 1 -1−1。
示例
输入
4
4 2 1 1
1
2
输出
4 2 1 3
限制条件
相邻的两个a[]中后一个必须是前一个的因数,必须满足a[i-1] % a[i] = 0,否则无法构造
证明 (ans[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 [ i − 1 ] = g c d ( a n s [ 1 ] , . . . a n s [ i − 1 ] ) a[i-1] = gcd(ans[1],...ans[i-1])a[i−1]=gcd(ans[1],...ans[i−1])
∴ a [ i ] = g c d ( a [ i − 1 ] , a n s [ i ] ) ∴ a[i] = gcd(a[i-1],ans[i])∴a[i]=gcd(a[i−1],ans[i])
∴ a [ i − 1 ] ∴ a[i-1]%a[i]=0, ans[i]%a[i]=0∴a[i−1]
由 a[i-1] % a[i] = 0可知,a是一个非增序列, **a[i - 1]与a[i]**只有两种情况, a[i - 1] == a[i], a[i - 1] > a[i]
当 a[i - 1] > a[i]
由于我们推导出的a[]是递减数列(不一定单调),意味着此时的 a[i] 是第一次出现, 那么数a[i]一定是没使用过的, 该位可以直接填a[i],因为a[i] = gcd(a[i - 1], a[i])是一定成立的,满足我们需要构造的数的条件
当 a[i - 1] == a[i]
那么需要找一个a[i]的未使用倍数
填多少倍:
与1的互换同理, 找一个最小的满足条件的倍数即可
从哪开始找:
不能直接从a[i]开始查找, 会超时
∵ a[i] = gcd(ans[1],…ans[i]) gcd()里的每一个数都是a[i]的倍数
∴ ans[i]是a[i]的倍数, ans[i-1]也是a[i]的倍数, 且 ans[i] > ans[i-1]
所以可以从ans[i-1]+a[i]开始查找
不能从a[i] + a[i] 开始找,如果a[]数组元素全部相同,由于查找上限是n,很容易运行超时
记住每一次取出一个数放入ans[i],就要记录该数为已使用
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n, a[N], ans[N];
bool used[N];
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int find(int i)
{
//查找a[i]的倍数
// ans[i] > ans[i-1], ans[i-1]也是a[i]的倍数
for (int f = ans[i - 1] + a[i]; f <= n; f += a[i])
{
if (!used[f] && gcd(a[i - 1], f) == a[i])
{
used[f] = true;
return f;
}
}
return -1;
}
void solve()
{
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> a[i];
for (int i = 1; i < n; i ++)
{
if(a[i - 1] % a[i] != 0) //初筛数组 不满足限制条件直接return
{
cout << -1 << endl;
return;
}
}
ans[0] = a[0]; //第一个数直接设为a[0]
used[a[0]] = true ;
for (int i = 1; i < n; i ++ )
{
if (a[i - 1] != a[i])
{
ans[i] = a[i];
used[ans[i]] = true;
}
else
ans[i] = find(i);
//cout << ans[i] << endl;
if (ans[i] == -1) //如果在find()中查找失败 return
{
cout << -1 << endl;
return;
}
}
for (int i = 0; i < n; i ++ ) cout << ans[i] << " ";
cout << endl;
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
2 背包 逻辑运算
E-小苯的01背包(easy)_牛客小白月赛94 (nowcoder.com)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 9;
int n , k , v[maxn] , w[maxn] , mx;
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; i++)
cin >> v[i] >> w[i]; //体积 价值
for(int s = 2000; s >= 0; s--) //枚举答案 答案不会超过最大的wi
{
int weight = (1 << 20) - 1; //按位与的初始条件
for(int i = 1; i <= n; i++)
{
if((s & w[i]) == s) //可以选的物品
weight = weight & v[i]; //体积按位与
}
if(weight <= k)
{
mx = s;
break;
}
}
cout << mx;
return 0;
}
2.5
F-小苯的01背包(hard)_牛客小白月赛94 (nowcoder.com)
上一题, 数据范围2e3, 可以直接从2000开始枚举答案
这一题, 数据范围1e9, 可以从高位开始, 用试填法, 逐位确定
#include<iostream>
#include<climits>
#include<vector>
using namespace std;
int v[200005], w[200005], maxn;
vector<int> memory;
//位运算
int main()
{
int n, k; cin >> n >> k;
for (int i = 1; i <= n; i++)
{
cin >> v[i] >> w[i];
}
for (int s = 30; s >= 0; s--)
{
int res = maxn | (1 << s);
int vol = INT_MAX;
for (int i = 1; i <= n; i++)
{
if ((w[i] & res) == res)
{
vol &= v[i];
}
}
if (vol <= k) maxn = res;
}
cout << maxn;
return 0;
}