2023-02-27 15:05阅读: 247评论: 0推荐: 1

AtCoder Beginner Contest 291

A - camel Case (abc291 a)

题目大意

给定一个字符串,找到其是大写字母的位置。

解题思路

逐位判断即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
string s;
cin >> s;
int pos = find_if(s.begin(), s.end(), [](char c){
return isupper(c);
}) - s.begin();
cout << pos + 1 << '\n';
return 0;
}


B - Trimmed Mean (abc291 b)

题目大意

给定5n个分数,去掉最高的 n 个和最低的n个,然后算剩下数的平均分。

解题思路

排序后选中间3n个数的平均值即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
vector<int> s(n * 5);
for(auto &i : s)
cin >> i;
sort(s.begin(), s.end());
int sum = accumulate(s.begin() + n, s.end() - n, 0);
cout << fixed << setprecision(10) << 1.0 * sum / (3 * n) << '\n';
return 0;
}


C - LRUD Instructions 2 (abc291 c)

题目大意

当前位置为(0,0),给定一个 LRUD操作序列,问在执行该序列时,所访问的点坐标是否重复。

解题思路

set记录下访问过的下标,加速查询,模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int x = 0, y = 0;
int n;
string s;
cin >> n >> s;
auto check = [&](){
set<pair<int, int>> visit;
visit.insert({x, y});
for(auto &i : s){
if (i == 'L')
-- x;
else if (i == 'R')
++ x;
else if (i == 'D')
-- y;
else if (i == 'U')
++ y;
else
assert(0);
if (visit.find({x, y}) != visit.end())
return true;
visit.insert({x, y});
}
return false;
};
cout << (check() ? "Yes" : "No") << '\n';
return 0;
}


D - Flip Cards (abc291 d)

题目大意

给定n张卡,每张卡正反两面都写了个数。

现在可以将一些卡面正反颠倒,问有多少种策略,使得前一张的反面和后一张卡的正面的数都不同。

解题思路

从搜索状态考虑,为了策略的合法性(前后两张卡反正面数不同)需要的状态为前一张卡是否翻转,其余信息都可以不需要。

因此设dp[i][0/1]表示前 i张卡,第 i张卡是没翻转/翻转,且前面卡面都符合题意要求的方案数。

转移就枚举第 i1位的翻转情况,根据是否符合题意要求。

因为每次仅涉及 i1的状态,第一维可以滚动数组优化。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mo = 998244353;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
vector<pair<int, int>> card(n);
for(auto &i : card)
cin >> i.first >> i.second;
vector<int> dp(2, 1);
for(int i = 1; i < n; ++ i){
vector<int> tmp(2, 0);
tmp[0] = dp[0] * (card[i].first != card[i - 1].first) + dp[1] * (card[i].first != card[i - 1].second);
if (tmp[0] >= mo)
tmp[0] -= mo;
tmp[1] = dp[0] * (card[i].second != card[i - 1].first) + dp[1] * (card[i].second != card[i - 1].second);
if (tmp[1] >= mo)
tmp[1] -= mo;
dp.swap(tmp);
}
cout << (dp[0] + dp[1]) % mo << '\n';
return 0;
}


E - Find Permutation (abc291 e)

题目大意

现在有一个1n排列。已知 m个大小关系,即第 ai个数小于第 bi个数。

问能否从这些关系中确定唯一的排列,可以则输出排列,不可以则输出 No

解题思路

初看该题时没啥思路,然后想着看看一些特别情况,比如如何确定1的位置。

一个位置是 1,那就是说该位置小于其他所有位置。而小于关系是有传递性的,于是考虑如果把这个传递性体现出来,来求得一个位置小于所有位置。

这就可以考虑一张由小于关系构成的有向图,a<b则第 a号点连一条边到第 b号点。

如果我能一个入度为 0的点出发,能到达 n1个点 ,那就意味着这个点小于其他所有点,此点就是1。当然如果入度为 0的点有多个,那么此时 这些点都可能是1,那就不行了。

1考虑完后,就去掉其影响,考虑 2,发现还是原来的子问题。

然后就发现就是一个拓扑排序,在这过程中,队列里(此时度数为 0的点)只能有一个(不然有多个点都可以取该值), 依次确定出1,2,3的位置。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> du(n);
vector<vector<int>> edge(n);
for(int i = 0; i < m; ++ i){
int u, v;
cin >> u >> v;
-- u, -- v;
edge[u].push_back(v);
++ du[v];
}
vector<int> ans(n);
auto check = [&](){
queue<int> team;
for(int i = 0; i < n; ++ i){
if (du[i] == 0)
team.push(i);
}
int cur = 0;
while(!team.empty()){
if (team.size() != 1)
return false;
int u = team.front();
++ cur;
ans[u] = cur;
team.pop();
for(auto &v : edge[u]){
du[v] --;
if (du[v] == 0)
team.push(v);
}
}
return (cur == n);
};
if (check()){
cout << "Yes" << '\n';
for(auto &i : ans)
cout << i << ' ';
}else {
cout << "No" << '\n';
}
return 0;
}


F - Teleporter and Closed off (abc291 f)

题目大意

给定n号点,依次排列,每个点都有一个数组 s,第 i个点的 si,j表示从i号点能否到达 i+j号点。

对于每个k=2,3,4,...,n1,问从 1号点出发,再不经过 k号点的情况下,到达第 n号点的最小距离。

解题思路

注意这题的s的长度m只有 10

如果不考虑k的话,就设 dp[i]表示到达 i号点的最小距离,因为只能往标号大的点走,因此转移就枚举下一个到达的点,取最小值即可。

但有k的话, dp[n]就不一定合法了,因为在转移的时候我们没有限制不能走第 k号点,所以不知最终的 dp[n]是否经过第 k号点。

但因为关键决策就是第 k号点,因此我们揪出转移会涉及到 k号点的点,只有m1个,即第 km+1,km+2,...,k1号点,对于这些点 l,我们枚举它们下一个到达的,且大于k的点 r,此时从1号点到 n号点分成了三段:

  • 1l
  • lr
  • rn

那此时从 1号点到 l号点, r号点到 n号点都是没有限制的,而这个可以事先预处理dis1[l]表示 1号点到 l号点的最短距离,以及 dis2[r]表示从 r号点到 n号点的最短距离。而l号点到 r号点的距离是 1,且保证了不经过 k号点,因此总的距离是 dis1[l]+dis2[r]+1

预处理 dis1[i],dis2[i]的时间复杂度是 O(nm),对于每个 k的时间复杂度是O(m2)(有 m个枚举点,每个点枚举下一个目的地有 m个),因此总的时间复杂度是 O(nm2)

这其实跟一张图,必定经过某条边的最短路是一样的想法。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, m;
cin >> n >> m;
vector<string> s(n);
for(auto &i : s)
cin >> i;
vector<int> dis1(n, n + 5), dis2(n, n + 5);
dis1[0] = 0;
for(int i = 0; i < n - 1; ++ i){
for(int j = 0; j < m && i + j + 1 < n; ++ j){
if (s[i][j] == '1')
dis1[i + j + 1] = min(dis1[i + j + 1], dis1[i] + 1);
}
}
dis2[n - 1] = 0;
for(int i = n - 2; i >= 0; -- i){
for(int j = 0;j < m && i + j + 1 < n; ++ j){
if (s[i][j] == '1')
dis2[i] = min(dis2[i], dis2[i + j + 1] + 1);
}
}
for(int i = 1; i < n - 1; ++ i){
int ans = n + 5;
for(int j = max(0, i - m + 1); j < i; ++ j){
for(int k = i - j; k < m; ++ k){
if (s[j][k] == '1')
ans = min(ans, dis1[j] + 1 + dis2[j + k + 1]);
}
}
if (ans == n + 5)
ans = -1;
cout << ans << " \n"[i == n - 2];
}
return 0;
}


G - OR Sum (abc291 g)

题目大意

两个长度为n的数组 AB

现在可以对 数组A进行左循环移位操作,即将 A的第一个元素放到最后。

i=0n1Ai|Bi的最大值。

解题思路

朴素的方法复杂度是O(n2),即有 O(n)种操作情况,每种情况的答案计算复杂度为 O(n)。考虑优化计算。

因为位运算各个数位独立,我们依次考虑每个数位1的数量,再乘以该数位的基(就是2i)就可以得到结果。

i=0logm2ij=0n1A(j+k)%ni|Bji

其中k就是进行了 k次操作。Aji就是 Aj在二进制下的第 i位的值,0或者 1

单看第二个求和式子感觉是个卷积式,只要把B颠倒一下,就是j=0n1A(j+k)%ni|Bnj1i

而因为 A|B=A+BA&B,因此j=0n1A(j+k)%ni+Bnj1iA(j+k)%ni&Bnj1i

前两项是定值,而后一项,因为其取值只有01与运算乘法运算是一样的结果,因此其可以看成是个卷积。

a=(A0,A1,...,An1,A0,A1,...,An1),b=(Bn1,Bn2,...,B1,B0),c=ab
此时cn+k1=i=0n1bi×an+k1i,和上面的&卷积式是一样的。

因此通过一次卷积,就能得到每个数位的移动k次后的 或运算的结果,每种操作的每个数位累计求和,求个最大值即可。卷积复杂度是O(nlogn),总的时间复杂度是 O(nlognlogm)

神奇的代码


Ex - Balanced Tree (abc291 h)

题目大意

<++>

解题思路

<++>

神奇的代码


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/17159767.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(247)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.