B. Equalize
题目:
思路:
思路很重要,但是实现也很重要
题目要我们对数组a加上一个排列p,使得其中相同数量的数字最多,那我们怎么做呢?
首先,我们可以先排号数组a,因为排列是可以随意放的,所以与数组a的顺序无关
接下来我们想如何最大化相同的数字呢?首先一个显然的最大长度是n,但是显然出题人不会让我们轻而易举获得n,继续观察,我们可以发现,如果一个子数组是连续的,且其中的 最大值 + 1 等于 最小值 + n 的话,那么这段可以获得的最大元素数量就是其长度 len 了,比如 1 4 5 100 100,那么我们就可以将 p 这样排 5 2 1 3 4,这样最大元素数量就是 3 个 6 了
所以我们可以在数组中找一个子数组,满足 a[r] - a[l] == n - 1,此时 r - l + 1 就是答案了,然后我们将其中的最大值输出即可,这里可以使用二分或双指针实现(我还以为双指针会超时。。)
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"void solve()
{int n;cin >> n;vector<int> a;set<int> has;for (int i = 0; i < n; i++){int x;cin >> x;if (!has.count(x)){a.push_back(x);}has.insert(x);}sort(a.begin(), a.end());int cnt = 0;int r = 0;for (int i = 0; i < a.size(); i++){while (r < a.size() && a[r] - a[i] <= n-1){r++;}cnt = max(cnt, r - i);}cout << cnt << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}
C. Physical Education Lesson
题目:
思路:
数学题,又是想到了实现上却有问题。。又复习了快速求因子
我们可以先可以观察一下这个循环长什么样,比如 k = 6 时,就是 1 2 3 4 5 6 5 4 3 2 1 2 3...如图
可以看到它是一个山峰的形式,那么对于其中任意一个数,只要它不是波峰或者波谷,那么他都有两种可能,我们把他们的形式用算式写出来
首先对于左边部分,对于一个数x,它的位置n与其数的关系为
n = x + m*T
其中T为周期,即 2 *(k-1),拆开发现,就是
也就是说,如果 2*m 是 n-x 的因数,那么就可以存在一个k满足题意
同样的我们写出右边的式子,这里直接化简,得到
同理,即 如果 2m 是 n+x-2 的因子,那么也存在符合条件的k
接下来我们考虑特殊情况,即峰的时候,既然我们这两个式子都符合峰,那么就会重复一次,因此我们要去重,这里我们使用set去重即可
特别的,如果算出来的k小于x,那么肯定不行,这时可不能算入答案中
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"void solve()
{int n, x;cin >> n >> x;set<int> ans;int c1 = n - x, c2 = n + x - 2;if (c1 % 2 == 0){c1 /= 2;for (int i = 1; i <= sqrt(c1); i++){if (c1 % i == 0){if (c1 / i + 1 >= x)ans.insert(c1 / i + 1);if (i + 1 >= x)ans.insert(i + 1);}}}if (c2 % 2 == 0){c2 /= 2;for (int i = 1; i <= sqrt(c2); i++){if (c2 % i == 0){if(c2 / i + 1 >= x)ans.insert(c2 / i + 1);if (i + 1 >= x)ans.insert(i + 1);}}} cout << (int)ans.size() << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}
C. Sasha and the Casino
题目:
思路:
倍投法,赌徒是孬的
先介绍一下倍投法
假如我们有一个神秘的盒子,我们可以往里面投任意数量的金币,每次有50%的几率消失,有50%的几率翻倍还给你,问你如何获得无限的钱,每次的几率是一样的,你只能投小于等于你当前持有钱数的钱
而倍投法就是解决这个题目用的,对于每次操作,我们只需要执行下面两次操作即可
①.如果上次赢钱了or这是第一次投钱,那我们就只投一个
②.如果我们上次输了,那我们就投上次投的钱的两倍,即 a[n]= 2 * a[n-1]
为什么这样是可行的呢?首先我们假设已经输了n-1次了,即输了 1 2 4 8 ... =
个金币
那么这一次我们只需要投 个金币,只要这次赢了,我们就能赚回之前所有输掉的金币,同时还会多1块钱,这样是赚的,同时,由于每次的概率都是一样的,连续输k次的概率为
,这是一个很小的概率,所以输的概率会越来越小,因此这种方法是可行的
那么回到这道题
我们也可以使用倍投法的思想,只不过这次不是两倍,而是k倍了,同时这次还有一个保底机制,即我们的钱只要能撑到n+1回合,那我们就能得到无穷无尽的钱
但是这道题问的是能不能得到任意的钱数啊?
其实只要我们能赢无数的钱,那我们输一个 当前钱数 - 目标钱数 的钱数,不久能得到目标钱数了嘛,所以无伤大雅
那么接下来就是推式子了
既然要满足一次性赚的钱能抵消之前所有花的的钱,那么就要满足以下式子
其中 c 为此次投的钱,sum为没投之前所花的钱
化简一下,得
则有
用代码表示就是 c = sum / (k - 1) + 1(加一是因为要大于)
那我们我们全用 sum 表示就是 sum += sum / (k - 1) + 1,其中前面的sum表示到投完当前次后要花的钱,后面的sum表示之前所花的钱的总数
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"void solve()
{int k, x, a;cin >> k >> x >> a;int sum = 0;for (int i = 1; i <= x+ 1; i++){sum += sum / (k - 1) + 1;if (sum > a){no;return;}}yes;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}
C. MEX Game 1
题目:
思路:
简单的思维题,但是我又是想到了没写好!!!
首先我们分析一下爱丽丝最大的MEX在没鲍勃时是多少,显然是第一个不存在数组中的元素,这个是无法改变的
那么接下来分析有bob会发生什么
我们可以观察到,每次鲍勃选完数后,如果这个数有剩余,那么爱丽丝可以直接选他选的这个数
也就是说爱丽丝的行为可以根据鲍勃做出适应,于是乎可以发现一个结论,只要这个数起码有两个,那么爱丽丝必能选到
那么再来分析一下只有一个的话呢?我们发现,如果只有一个数只有一个,那么爱丽丝一开始选这个即可,否则如果有两个及以上的只有一个的数,鲍勃可以删除另一个只有一个数的数,因此我们就可以定义一个变量,其用来保存遇到了几个只有一个数的数,如果它等于2,那么此时这个数就是答案
代码很简单
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"void solve()
{int n;cin >> n;vector<int> a(n);map<int, int> has;for (int i = 0; i < n; i++){cin >> a[i];has[a[i]]++;}int once = 0;for (int i = 0; i <= n; i++){once += (has[i] == 1);if (once == 2 || !has[i]){cout << i << endl;return;}}
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}
C. XOR-distance
题目:
思路:
挺好的贪心+位运算题
这一题让我们求 |a ^ x - b ^ x|的最小值,我们既然要求最小值,那肯定是要让两个数异或之后越接近越好,一个显然的想法肯定是贪心,我们要贪心的构造出两个相差最小的数,那么如何构造呢?
假设a和b的前 i 位都相等,那么我们就不用管了,一直找到第一个不相等的地方,那么这个地方我们要不要改变呢?
如果题目没有要求r的话,我们肯定是改变比较好的,但是现在有了限制,那我们就要考虑一下了,假设改变了第一个不相等的地方,那么肯定是不够好的,因为改变之后首先我们的r就减少了很多,这不利于我们后续的进行,所以贪心的想我们这一位不如不取,并且如果这一位不取的话,后面的所有位我们都可以取(前提是这位是可以取但是不取的情况)
随后我们就可以模拟了,如果不一样,那我们就观察其符号
如果一开始的数是正数,那我们后面的数必须都得是负数,反之亦然
具体实现看代码
(或者我们可以令a恒大于b,这样的话更好操作,同时也更好证明为什么不取第一位,因为如果取了第一位,ab大小会互换,而我们后面的所有操作就其实是原a大于b的操作反过来,而这时我们还多消耗了一位没必要的,所以肯定不行)
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"void solve()
{int a, b, r;cin >> a >> b >> r;int ans = 0;bool Findmax = false;for (int i = 60LL; i >= 0LL; i--){int x = a >> i & 1;int y = b >> i & 1;int fuhao = x - y;if (x == y){continue;}if (!Findmax){ans = (1LL << i) * fuhao;Findmax = true;}else{if (r >= (1LL << i) && fuhao * ans > 0LL){r -= (1LL << i);ans -= fuhao * (1LL << i);}else{ans += fuhao * (1LL << i);}}}cout << abs(ans) << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}