nclg2022届选拔赛题解A~D

 

A 游戏

原题:CF1221A

题目:

小华非常喜欢玩2048,现在他分享了一个2048pro版给你玩,你能通关吗?

最初你有一个n个整数的集合s。集合中的每一个整数都是2的幂(1,2,4,8,16,32 .......)。

您可以在这个集合中执行任意数量(可能是零)的操作。

在每个操作中,你从s中选择两个相等的整数,将它们从s中移除,并将等于它们的和的数字插入s中。

例如,如果s={1,2,1,1,4,2,2},你选择移除(2,2),那么集合就变成了{1,1,1,4,4,2}。

经过若干次操作后,如果数字2048在集合s中,你就通关了。

输入
第一行包含一个整数q(1≤q≤100)——包含q组数据。

每组数据的第一行包含一个整数n(1≤n≤100)- s中的元素数量。

第二行包含n个整数s1,s2,…,sn(1≤si≤2^29)-集合s中的元素。可以保证集合的所有元素都是2的幂。

输出
对于每个查询,如果有可能获得数字2048,则输出YES
否则输出 NO

样例解释

在第一组数据中,您可以通过以下方式获胜:选择(512,512), s变成{1024,64,1024}。然后选择(1024,1024), s变成 {2048,64} 你赢了。

在第二组数据中,s最初包含2048。

题解:

思路之一:用优先队列直接暴力枚举。

#define YES cout << "YES" << endl;
#define NO cout << "NO" << endl;
priority_queue<int, vector<int>, greater<int> > q; // 小根堆,队头为最小值 void solve() { while(q.size()) q.pop(); // 清空队列 int n; cin >> n; for(int i = 0; i < n; i ++) { int x; cin >> x; q.push(x); } while(1) { int a = q.top(); q.pop(); // 拿出第一个最小值 if(a == 2048) { YES return; } if(a > 2048 || q.size() == 0) break; // 队列为空或者最小值大于2048 if(a == q.top()) // 如果第二小的值等于第一小,那么继续拿出 { int b = q.top(); q.pop(); q.push(a*2); if(b == 2048) { YES return; } } } NO // 没有凑出2048 }

 

B 上升序列

原题:CF920C

小华很喜欢单调递增的序列,你能帮帮他吗?

你有一个数组a,由n个整数组成。从1到n的每个整数在这个数组中只出现一次。

即数组a中的元素是1~n的排列。

对于下标i(1 ≤ i ≤ n - 1),可以将第i个元素与第(i + 1) 个元素交换,对于其他下标则不可以。您可以执行任意数量的交换操作。第i个元素与第(i + 1)个元素交换的次数没有限制(如果位置没有被禁止)。

你能通过执行一些交换操作让这个数组按升序排序吗?

输入
第一行包含一个整数n(2≤n≤200000)-数组中的元素数量。

第二行包含n个整数a1, a2,…, an(1≤ai≤200000)——数组的元素。从1到n的每个整数恰好出现一次。

第三行包含一个n - 1个字符的字符串,每个字符都是0或1。如果第i个字符为1,则第i个元素可以与第(i + 1)个元素交换任意次数,否则禁止与第(i + 1)个交换。

输出
如果可以通过执行一些交换操作让这个数组按升序排序,则输出YES。否则,输出 NO。

样例解释

在第一个样例中你可以先交换5和3,再交换4和5即可得到升序序列。

 

题解:

思路之一:贪心。

因为a中的元素是1~n的排列,所以a的递增序列即为(1,2,3,...,n)。 并且可以证明在一段连续可操作区间内你可以通过若干次操作使得该区间内的数任意排列。则当当前元素不能进行操作时,只需判断当前序列最大值是否会等于该位置目的递增序列的值即可。

#define YES cout << "YES" << endl;
#define NO cout << "NO" << endl;
const
int N = 2e5 + 10; int a[N]; void solve() { int n,ma = 0; cin >> n; for (int i = 0; i < n; i ++ ) cin >> a[i]; string s; cin >> s; for (int i = 0; i < n-1; i ++ ) { ma = max(ma, a[i]); // 更新当前序列最大值 if(s[i] == '0' && i+1 != ma) // 判断是否不能构造 { NO return; } } YES }

 

C 最小公倍数(简单版)

原题:CF1497C1

本题与 困难版 的不同之处是在本题中,k=3

小华很喜欢最小公倍数,下面是他遇到的与最小公倍数相关的难题。

小华想了很久都没有思路。你能 帮他解决吗?

给定一个正整数n,找出k个正整数a1,a2,…,ak,使满足:

1:a1 + a2 +…+ ak = n。
2:LCM(a1,a2,…,ak) ≤ n/2。
这里LCM是a1 a2…ak的最小公倍数。

可以证明对于给定的约束条件,答案总是存在的。

输入
第一行包含一个整数t
(1≤t≤10000)——测试用例的数量。

每个测试用例的唯一 一行包含两个整数n, k(3 ≤ n ≤ 1000000000,k = 3)。

输出
对于每个测试用例,打印k个满足所有条件的正整数a1,a2, a3。

题解:

思路:很明显,这是一道构造题。对于构造题,一定会有一种通解,玄学猜结论。

如果n为奇数,很明显能分成 1,n/2,n/2,lcm = n/2;

如果n为偶数,那要分情况讨论:

  • 如果 n/2 为偶数,那么很明显可以拆为 n/2,  n/4,  n/4。lcm = n/2
  • 如果 n/2 为奇数,此时 n >= 6。可以发现此时 n/2-1 一定为偶数。那么我们就可以拆分为 n/2-1,  n/2-1,  2。 lcm = n/2-1。

上述构造方法同样适用于困难版。

 

void solve()
{
    int n,k;
    if(n & 1)
    {
        cout << 1 << " " << n/2 << " " << n/2 << endl;
    }
    else if( (n/2) & 1 )
    {
        cout << 2 << " " << n/2-1 << " "<< n/2-1 << endl;
    }
    else cout << n/2 << " " << n/4 << " " << n/4 << endl;
}

 

最小公倍数(困难版)

原题:CF1497C2

本题与 简单版 的不同之处是在本题中,k<=n。

如果你解决了简单版本,小华十分开心,但是小华发现修改了一个条件后他又不会了。

你能帮帮他吗?

给定一个正整数n,找出k个正整数a1,a2,…,ak,使满足:

1:a1 + a2 +…+ ak = n。
2:LCM(a1,a2,…,ak) ≤ n/2。
这里LCM是a1 a2…ak的最小公倍数。

可以证明对于给定的约束条件,答案总是存在的。

输入
第一行包含一个整数t
(1≤t≤10000)——测试用例的数量。

每个测试用例的唯一一行包含两个整数n, k(3≤n≤1000000000,3≤k≤n)。

可以保证所有测试用例的k的和不超过100000。

输出
对于每个测试用例,打印k个满足所有条件的正整数a1,a2,…,ak。

题解

思路:因为 1 是不会影响序列的最小公倍数的,所以解法和简单版一样可以先输出 k-3 个1,剩下3个就和简单版一样输出即可。

void solve()
{
    int n,k;
    cin >> n >> k;
    for(int i = k; i > 3; i --) cout << 1 << " ";
    n-=(k-3);
    if(n & 1)
    {
        cout << 1 << " " << n/2 << " " << n/2 << endl;
    }
    else if( (n/2) & 1 )
    {
        cout << 2 << " " << n/2-1 << " "<< n/2-1 << endl;
    }
    else cout << n/2 << " " << n/4 << " " << n/4 << endl;
}

 

posted @ 2023-05-04 18:48  陌上&初安  阅读(186)  评论(0)    收藏  举报