Contestant. Rank 1877. Rating +196.
A. camel Case
题意
给定一个由一个大写字母和若干个小写字母组成的字符串,输出大写字母的位置。
思路
如题,很签到。
时间复杂度:\(O(n)\)
对应AC代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
string s;
cin >> s;
for(int i=0;i<s.size();i++){
if(s[0] >= 'A' && s[i] <= 'Z'){
cout << i + 1 << '\n';
break;
}
}
}
无聊的签到题
B. Trimmed Mean
题意
给定 \(5N\) 个评委的分数,去掉最大 \(N\) 个和最小 \(N\) 个评委的分数,输出剩余分数的平均数。
思路
如题,排个序即可。
时间复杂度:\(O(n \log n)\)
对应AC代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int a[N];
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
for(int i=0;i<5 * n;i++) cin >> a[i];
sort(a, a + 5 * n);
double ans = 0;
for(int i=n;i<5*n-n;i++) ans += (double) a[i];
cout << (ans / ((double) 3 * n)) << '\n';
}
差点看成只各去掉一个((
C. LRUD Instructions 2
题意
给定一个由 \(L,R,U,D\) 组成的字符串,模拟点的移动,\(L\) 表示横坐标减一,\(R\) 表示横坐标加一,\(D\) 表示纵坐标减一,\(U\) 表示纵坐标加一。输出有多少点被经过了至少两遍。
思路
如题,模拟即可。
可以用 \(map\) 存有没有经过,或者开一个布尔数组。
时间复杂度:\(O(n)\)
对应AC代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int a[N];
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
map<pii, bool> mp;
int n;
cin >> n;
string s;
cin >> s;
pii p = {0, 0};
mp[p] = true;
bool f = false;
for(int i=0;i<n;i++){
char now = s[i];
if(now == 'L'){
p.first --;
}else if(now == 'R'){
p.first ++;
}else if(now == 'U'){
p.second ++;
}else p.second --;
if(mp[p]){
f = true;
break;
}
mp[p] = true;
}
cout << (f ? "Yes\n" : "No\n");
}
捏马的,忘了在走过以后设标记了((
D. Flip Cards
题意
给定 \(n\) 张牌,牌的正反两面各印有一个数字,输出整个牌组满足条件的正反情况,满足相邻牌值不相等。
思路
考虑到只需满足相邻牌值不相等,所以我们不妨采用 \(dp\) 的方式,定义一个 \(dp[i][j]\) 表示第 \(i\) 位及以前的牌满足第 \(i\) 张牌的正反情况满足 \(j\) 的情况总数,\(j=0\) 表示向上,否则向下。
那么,对于一个位置,它有两种递推的方式——从上一个为正面的牌的情况数和从上一个为反面的牌的情况数。两个递推的方式的和即为当前位置的值。
如,如果不考虑相邻牌不相等,那么
\(dp[i][0] = dp[i - 1][0] + dp[i - 1]][1], dp[i][1] = dp[i - 1][0] + dp[i - 1][1]\)。
考虑条件的话,我们用 \(if\) 判断即可。
当然,初始情况下 \(dp[1][0] = dp[1][1] = 1\)。
时间复杂度:\(O(2n)\)
对应AC代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N = 2e5 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int dp[N][2];
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
int lastA, lastB;
cin >> lastA >> lastB;
dp[1][0] = 1, dp[1][1] = 1;
for(int i=2;i<=n;i++){
int nowA, nowB;
cin >> nowA >> nowB;
if(nowA != lastA) dp[i][0] = (dp[i][0] + dp[i - 1][0]) % mod;
if(nowA != lastB) dp[i][0] = (dp[i][0] + dp[i - 1][1]) % mod;
if(nowB != lastA) dp[i][1] = (dp[i][1] + dp[i - 1][0]) % mod;
if(nowB != lastB) dp[i][1] = (dp[i][1] + dp[i - 1][1]) % mod;
lastA = nowA, lastB = nowB;
}
cout << (dp[n][0] + dp[n][1]) % mod << '\n';
}
其实只有两个的话完全可以用两个变量来存,使空间复杂度降低很多
E. Find Permutation
题意
给定 \(n\) 个点以及 \(m\) 组可重复的大小关系 \(A_i, B_i\),构建一个排列 \(p\),对于所有条件,均满足 \(p_{A_i} < p_{B_i}\)。输出是否能唯一确定这个排列,若能唯一确定,输出这个排列。
思路
首先,我们可以一眼看出这是求拓扑序。那么,这道题唯一的障碍就是怎么排除无法唯一确定的点,因为如果成环了,拓扑排序会自动找出。
那么,我们来考虑每次读取队列的时候的情况:
考虑到拓扑排序的算法,我们会将每次遍历的时候,将入度为 \(0\) 的点全都放入队列,那么只要出现多个入度为 \(0\) 的点,那么就一定会出现如 \(a \rightarrow b, a \rightarrow c, b\ ?\ c\) 的情况,此时直接判 \(No\) 即可。
时间复杂度:\(O(m)\)
对应AC代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N = 2e5 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int n, m;
vector<int> G[N];
int in[N]; // 存储每个结点的入度
void topSort() {
vector<int> L;
queue<int> q;
for (int i = 1; i <= n; i++)
if (in[i] == 0) q.push(i);
if(q.size() > 1){
cout << "No\n";
return;
}
while (!q.empty()) {
int u = q.front();
q.pop();
L.push_back(u);
for (auto v : G[u]) {
if (--in[v] == 0) {
q.push(v);
}
}
if(q.size() > 1) {
cout << "No\n";
return;
}
}
if (L.size() == n) {
cout << "Yes\n";
vector<int> ans(n);
for(int i=0;i<n;i++){
ans[L[i] - 1] = i + 1;
}
for (auto i : ans) cout << i << ' ';
} else {
cout << "No\n";
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m;
map<pii, bool> mp;
for(int i=0;i<m;i++){
int x, y;
cin >> x >> y;
if(mp[{x, y}]) continue;
G[x].emplace_back(y);
in[y] ++;
mp[{x, y}] = true;
}
topSort();
}
有一说一,为什么入读和出度的配对不可以用来判呢?
F. Teleporter and Closed off
题意
给定 \(n\) 个长度为 \(m\) 的字符串 \(s\),若 \(s_{i,j} = 1\),那么可以使用这个传送点,从第 \(i\) 个城市传送到第 \(i+j\) 个城市。输出对于第 \([2, n - 2]\) 个城市,从第一个城市到最后一个城市,满足跳过这个城市的路径使用的传送点的最少数量。
思路
首先,对于第 \(i\) 个城市,它的状态是从前一个传送点推过来的,存在递推性,所以我们可以用 \(dp\) 来实现。
对于第 \(k\) 个城市,若要跳过它,那么我们一定得从 \(i=k-x+1\) 个城市传送到 \(j = i + x\) 个城市,所以我们可以枚举 \(i \in [k - m + 1, k - 1], j \in [k + 1, i + m]\),找出对于所有城市 \(i\) 前面用了多少传送点,城市 \(j\) 后面用了多少传送点,最后的答案即为传送点数量之和 \(+1\)。
那么,我们可以从前往后 \(dp\),再从后往前 \(dp\),最后的值即为 \(dp_0[i] + dp_1[j] + 1\)。
而对于 \(dp\),从前向后的时候,对于第 \(i\) 个城市,我们可以枚举所有 \(j \in [1, m]\),那么我们可以传送到所有满足条件的 \(i + j\) 个城市。因而,这些城市的传送点数量即可用当前城市来更新。
从后往前是类似的。
时间复杂度:\(O(nm)\)
对应AC代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N = 4e5 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int dp[N][2];
string a[N];
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, m;
cin >> n >> m;
for(int i=0;i<n;i++)cin >> a[i];
memset(dp, 0x3f, sizeof dp);
dp[0][0] = dp[n - 1][1] = 0;
for(int i=0;i<n;i++){
for(int j=1;j<=min(m, n - i);j++){
if(a[i][j - 1] == '1') dp[i + j][0] = min(dp[i + j][0], dp[i][0] + 1);
}
}
for(int i=n-1;i>=0;i--){
for(int j=1;j<=min(m, i - 1);j++){
if(a[i - j][j - 1] == '1') dp[i - j][1] = min(dp[i - j][1], dp[i][1] + 1);
}
}
for(int k=1;k<n-1;k++){
int ans = inf;
for(int i=max(k - m + 1, 0ll);i<k;i++){
for(int j=k+1;j<min(n, i+m+1);j++){
if(a[i][j - i - 1] == '1') ans = min(ans, dp[i][0] + dp[j][1] + 1);
}
}
cout << (ans >= inf ? -1 : ans) << ' ';
}
}
dp老写错状态,淦