文章目录
- 前言
- A Once In My Life
- B 扫雷 1
- F 优秀字符串
- H 随机栈
- J 排列与合数
- L Toxel 与 PCPC II
- M 有效算法
前言
这是大一博主第一次参加xcpc比赛,虽然只取得了铜牌,但是收获满满,在了解了和别人的差距后会更加激励自己去学习,下面我把我们队会写的题目来给大家讲解,有代码有思路超详细!
A Once In My Life
这一题是一个构造题,我们需要构造一个幸运数,让这个幸运数是n的倍数。
那么我们先来思考,什么是幸运数?是不是必须包含123456789,还需要至少包含两个d,那么13456789d肯定是一个幸运数。但是这个数字不一定是n的倍数,那么我们如何在不改变12346789d的结构的同时让这个数字变为n的倍数呢?根据观察k和n的范围,我们不难猜想,我们可以再1323456789d后面补上n的长度个0,就比如n是233,那么我们先来一个数字等于123456789d000,补上三位0,此时这个数字仍然不是n的倍数,但是我们让这个数字加上n,再减去这个数字除n的余数,此时这个数字就一定是n的倍数,那么k就等于这个数除n即可得到答案。(减去这个数字除n的余数不会改变前面123456789d这个结构)
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define for1 for(int i = 1;i <= n;i++)
#define for0 for(int i = 0;i < n;i++)
#define forn1 for(int j = 1;j <= n;j++)
#define forn0 for(int j = 0;j < n;j++)
#define form1 for (int j = 1; j <= m; j++)
#define form0 for (int j = 0; j < m; j++)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define arrn int* arr = new int[n+2];arr[0] = 0,arr[n+1]=0
#define carr cin >> arr[i]
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define endl '\n'
int main() {
IOS;
int _;
cin >> _;
while (_--)
{
int n;
int d;
cin >> n >> d;
int len = to_string(n).size();
long long luck = 1ll*1234567890 + d;
luck *= pow(10, len);
luck = luck + n;
luck -= luck % n;
long long ans = luck / n;
cout << ans << endl;
}
return 0;
}
B 扫雷 1
这一题是一个贪心题但是不太难,我们贪心的思路就是我们从第一个格子开始走,如果走到的位置的价格是最小的(这里最小的意思是如果继续往后走就再也遇不见这么便宜的价格的意思),那么我们就选择在这里购买。这个是贪心的思路,那么我们怎么判断某个位置的最小值呢?(最小值的意思和刚才一样)?我们可以构建一个dp表dp[i],表示从i出发走到结尾,能遇见的最小值,转移方程为dp[i] = min(dp[i+1],arr[i]);
题解如下:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define for1 for(int i = 1;i <= n;i++)
#define for0 for(int i = 0;i < n;i++)
#define forn1 for(int j = 1;j <= n;j++)
#define forn0 for(int j = 0;j < n;j++)
#define form1 for (int j = 1; j <= m; j++)
#define form0 for (int j = 0; j < m; j++)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define arrn int* arr = new int[n+2];arr[0] = 0,arr[n+1]=0
#define carr cin >> arr[i]
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define endl '\n'
#define mod 1000000007
#define t() int _; cin >> _; while (_--)
int arr[200010];
int dp[200010];
int main() {
IOS;
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
}
for (int i = n; i > 0; i--)
{
if (i == n)
{
dp[i] = arr[i];
}
else
{
dp[i] = min(dp[i + 1], arr[i]);
}
}
int money = 0;
int ans = 0;
for (int i = 1; i <= n; i++)
{
money++;
if (arr[i] == dp[i] && money >= arr[i])
{
ans += (money / arr[i]);
money %= arr[i];
}
}
cout << ans << endl;
return 0;
}
F 优秀字符串
这一题是一个签到题,全场都通过了。
这题的思路就和题目给你的一样按照题目说的做就行了。
题解如下:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define for1 for(int i = 1;i <= n;i++)
#define for0 for(int i = 0;i < n;i++)
#define forn1 for(int j = 1;j <= n;j++)
#define forn0 for(int j = 0;j < n;j++)
#define form1 for (int j = 1; j <= m; j++)
#define form0 for (int j = 0; j < m; j++)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define arrn int* arr = new int[n+2];arr[0] = 0,arr[n+1]=0
#define carr cin >> arr[i]
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define endl '\n'
#define mod 1000000007
#define t() int _; cin >> _; while (_--)
int main() {
IOS;
int t;
cin >> t;
int ans = 0;
while (t--)
{
string s;
cin >> s;
set<char>a;
if (s.size() != 5)
continue;
if (s[2] != s[4])
continue;
for (int i = 0; i <= 3; i++)
{
a.insert(s[i]);
}
if (a.size() == 4)
ans++;
}
cout << ans << endl;
return 0;
}
H 随机栈
这题是一道简单的数学题,思路非常简单,我们出栈的时候出去的元素必须是当前栈里面最小的值,比如栈里面有1,2,3我们想让最终序列是非单调递减的话就必须先拿1,我们每次出栈的时候只需要求出取出当前最小值的概率即可,代码实现的话我们是用了一个map<int,int>键值表示入栈的元素,实值表示该元素的个数,入栈的话我们就mp[元素的值]++,出栈的时候就
mp[元素的值]–,mp中的最小值就是mp.begin().first,需要注意的是我们需要一个变量来记录已经出栈的元素中的最大值,如果当前栈的最小的元素要比这个变量的值大,那么我们直接输出0即可,因为已经不可能构造出非递减序列了。
J 排列与合数
首先经过观察不难发现只要结尾是0或者2或者4或者6或者8的五位数一定是个合数因为他们都是2的倍数,如果这个五位数没有一个数字是02468,那么就输出97531,题目最后的样例给了这是个合数,那么我们的思路就是遍历这5个数字如果某一位的数字是02648之一那么就交换这一位和结尾哪一位,(需要注意如果结尾是0的话就不能交换因为有可能会导致前导0的出现例如21350会变成01352)
题解如下:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define for1 for(int i = 1;i <= n;i++)
#define for0 for(int i = 0;i < n;i++)
#define forn1 for(int j = 1;j <= n;j++)
#define forn0 for(int j = 0;j < n;j++)
#define form1 for (int j = 1; j <= m; j++)
#define form0 for (int j = 0; j < m; j++)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define arrn int* arr = new int[n+2];arr[0] = 0,arr[n+1]=0
#define carr cin >> arr[i]
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define endl '\n'
#define mod 1000000007
#define t() int _; cin >> _; while (_--)
int main() {
IOS;
int t;
cin >> t;
while (t--)
{
string s;
cin >> s;
bool f = false;
for (int i = 0; i < 5; i++)
{
if (s[4] == '0')f = true;
if ((s[i] == '2' || s[i] == '4' || s[i] == '6' || s[i] == '8' || s[i] == '0')&&s[4]!='0')
{
f = true;
swap(s[i], s[4]);
}
}
if (f)cout << s << endl;
else cout << "97531" << endl;
}
return 0;
}
L Toxel 与 PCPC II
这一题是一个简单的动态规划题,我们建立dp表dp[i]表示排查到第i个bug时所需的最小时间,我们可以思考第i个bug可以怎么修复,他要么自己单独修复,要么跟着第i-1个一起修复,要么跟着第i-2个一起修复…跟着第1个一起修复,那么我们就可以再来一个循环来判定第i个跟着谁一起修复是最小的。但是这样的话复杂度会变为On方,所以我们要进行优化,根据提议n的范围是2e5,非常小,我们很容易想到如果连续修复太多个bug的话这个数得四次方是远大于从头开始扫描的时间,经过打表观察22的四次方刚好是大于2e5最小整数,所以如果要同时修复的bug大于22时我们肯定是不选的,我们只要求同时修复bug数小于22的,复杂度可以大大降低。我们只需要从max(1,i-22)到 i 这个区间选出最小值。(需要注意的是dp[0]记得开为0,并且需要long long)
题解如下:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define for1 for(int i = 1;i <= n;i++)
#define for0 for(int i = 0;i < n;i++)
#define forn1 for(int j = 1;j <= n;j++)
#define forn0 for(int j = 0;j < n;j++)
#define form1 for (int j = 1; j <= m; j++)
#define form0 for (int j = 0; j < m; j++)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define arrn int* arr = new int[n+2];arr[0] = 0,arr[n+1]=0
#define carr cin >> arr[i]
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define endl '\n'
#define mod 1000000007
#define t() int _; cin >> _; while (_--)
int arr[200010];
vector<long long>dp(200010, LONG_MAX);
int main() {
IOS;
int n,m;
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
}
dp[0] = 0;
dp[1] = 1ll*arr[1] + pow(1, 4);
for (int i = 2; i <= n; i++)
{
int j = 1;
if (i - j + 1 >= 22)
j = i - 21;
for(;j<=i;j++)
dp[i] = min(dp[j - 1] + arr[i] + (i-j+1)* (i - j + 1)* (i - j + 1)* (i - j + 1),dp[i]);
}
cout << dp[n] << endl;
return 0;
}
M 有效算法
本题考验二分知识,思路是二分k的取值,就按第一组样例来说当我们k取值为1的时候我们遍历数组想让|8-x|<=k1的话x的取值范围是7-9,想让|3-x|<=k2的话x的取值范围是1-5,两者x的区间不重合,说明肯定没有x能同时让|8-x|<=k1和|3-x|<=k2,所以不成立,当k=2的时候我们发现每一组x的区间都有重合的地方,那么此时a数组一定是可以全都变成x的,并且当k>2时毫无疑问绝对都可以符合,k的取值是否达标具有单调性,所以可以用二分来枚举。
题解如下:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define for1 for(int i = 1;i <= n;i++)
#define for0 for(int i = 0;i < n;i++)
#define forn1 for(int j = 1;j <= n;j++)
#define forn0 for(int j = 0;j < n;j++)
#define form1 for (int j = 1; j <= m; j++)
#define form0 for (int j = 0; j < m; j++)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define arrn int* arr = new int[n+2];arr[0] = 0,arr[n+1]=0
#define carr cin >> arr[i]
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define endl '\n'
#define mod 1000000007
#define t() int _; cin >> _; while (_--)
int a[300010];
int b[300010];
int n;
bool cheak(int k)
{
long long ml = a[1] - 1ll * b[1] * k;
long long mr = a[1] + 1ll * b[1] * k;
for (int i = 2; i <= n; i++)
{
long long ll = a[i] - 1ll * b[i] * k;
long long rr = a[i] + 1ll * b[i] * k;
if (ll > ml)ml = ll;
if (rr < mr)mr = rr;
}
if (mr < ml)return false;
return true;
}
int main() {
IOS;
int t;
cin >> t;
while (t--)
{
cin >> n;
for(int i = 1; i <= n; i++)
cin >> a[i];
for(int i = 1; i <= n; i++)
cin >> b[i];
int l = 0, r = 1e9;
while (l <= r)
{
int mid = (l + r) >> 1;
if (cheak(mid))r = mid-1;
else l = mid+1;
}
cout << l << endl;
}
return 0;
}
其余的题目前我还不会,等我补完咯再讲吧,如果觉得博主讲的不错请不要忘记给博主一个免费的关注,点赞,收藏支持一下哦~。