2023-04-23 01:03阅读: 699评论: 2推荐: 8

AtCoder Beginner Contest 299

A - Treasure Chest (abc299 a)

题目大意

给定一个包含 |*.的字符串,其中|两个,*一个,问*是否在两个|之间。

解题思路

找到两个|的下标l,r以及 *的下标mid,看看是否满足 l<mid<r即可。

神奇的代码
#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;
string s;
cin >> n >> s;
int l = s.find('|'), r = s.find('|', l + 1), m = s.find('*');
if (m > l && m < r)
cout << "in" << '\n';
else
cout << "out" << '\n';
return 0;
}


B - Trick Taking (abc299 b)

题目大意

给定n个人的卡片,颜色为 ci,数字为 ri

如果其中有颜色为 T的牌,则该颜色中数字最大的卡片对应的人赢。如果没有,则颜色为第一个人的卡牌颜色(即c0)中数字最大的卡片对应的人赢。

问谁赢。

解题思路

按照题意的两种情况,分别判断即可。

神奇的代码
#include <bits/stdc++.h>
#include <vector>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, t;
cin >> n >> t;
int maxx = 0;
int win = 0;
bool ok = false;
vector<int> c(n);
for(int i = 0; i < n; ++ i){
cin >> c[i];
ok |= (c[i] == t);
}
if (!ok)
t = c[0];
for(int i = 0; i < n; ++ i){
int r;
cin >> r;
if (c[i] == t){
if (maxx < r){
maxx = r;
win = i;
}
}
}
cout << win + 1 << '\n';
return 0;
}


C - Dango (abc299 c)

题目大意

定义一种字符串s的等级X(是一个正整数), 满足仅包含 -o,且头或尾仅一处为-,其余都为o。其等级Xo的数量。

给定一个字符串T,问其子串的最大等级。

解题思路

依次遍历字符串T,遇到两个 -时期间就有一个等级。

然后再考虑从头到第一个-的子串,从最后一个-到尾的子串的等级。

注意单纯的一个-并不是合法的(0不是正整数)

神奇的代码
#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;
string s;
cin >> n >> s;
int la = 0;
int ans = 0;
for(int i = 0; i < n; ++ i){
if (s[i] == '-'){
ans = max(ans, i - la);
la = i;
}
}
if (int pos = s.find('-'); pos != string::npos){
ans = max(ans, n - la);
ans = max(ans, pos + 1);
}
if (ans == 1)
ans = 0;
cout << ans - 1 << '\n';
return 0;
}


D - Find by Query (abc299 d)

题目大意

交互题。

这里有个长度为n01字符串 s ,其中s1=0,sn=1

你可以询问si的值。

输出一个位置 p满足 spsp+1

给定字符串长度n,你最多问20次。 n2×105

解题思路

感觉好像和某次 cf的交互题很像。

注意题意保证了s1=0,sn=1

首先询问中间位置mid=n2,如果smid=1,由于sn=1,最坏情况很有可能这后半部份都是 1,显然我们不该去问。但因为 s1=0,smid=1,所以前半部份必定有一处sp=0,sp+1=1。 反之smid=0的情况同理。

这样,通过一次询问,我们可以把答案保证存在的区间砍半了。那最多砍 logn次就找到结果了。由于 n2×105,所以不会超过20次。

神奇的代码
#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;
int l = 1, r = n;
while(l + 1 < r){
int mid = (l + r) >> 1;
cout << "? " << mid << endl;
int ok;
cin >> ok;
if (ok)
r = mid;
else
l = mid;
}
cout << "! " << l << endl;
return 0;
}


E - Nearest Black Vertex (abc299 e)

题目大意

给定一张图,要求给点涂黑白色,要求至少有一个黑点,且满足k个要求。

每个 要求 (pi,di)表示点 pi距离黑点的最近距离恰好为 di

点数、边数 2000

解题思路

注意边数只有2000

我们可以对每个要求的 pi进行 BFS,把距离其小于 d的点都标记为白色。

然后再对每个要求的 pi进行 BFS,把距离其为d的且未被标记为白色的点标记为黑色。

如果有个要求没有找到可以被涂黑色的点,就无解了。

神奇的代码
#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<vector<int>> edge(n);
for(int i = 0; i < m; ++ i){
int u, v;
cin >> u >> v;
-- u, -- v;
edge[u].push_back(v);
edge[v].push_back(u);
}
int k;
cin >> k;
vector<int> forbid(n);
vector<int> col(n);
vector<array<int, 2>> rule(k);
auto BFS = [&](int s, int d){
if (d == 0)
return;
vector<int> dis(n, -1);
queue<int> team;
dis[s] = 0;
team.push(s);
while(!team.empty()){
int u = team.front();
forbid[u] = 1;
team.pop();
for(auto &v : edge[u]){
if (dis[v] != -1)
continue;
dis[v] = dis[u] + 1;
if (dis[v] < d)
team.push(v);
}
}
};
for(auto &[p, d] : rule){
cin >> p >> d;
-- p;
BFS(p, d);
}
bool ok = true;
auto paint = [&](int s, int d){
vector<int> dis(n, -1);
queue<int> team;
dis[s] = 0;
team.push(s);
while(!team.empty()){
int u = team.front();
team.pop();
if (!forbid[u] && dis[u] == d){
col[u] = 1;
return true;
}
for(auto &v : edge[u]){
if (dis[v] != -1)
continue;
dis[v] = dis[u] + 1;
if (dis[v] <= d)
team.push(v);
}
}
return false;
};
for(auto &[p, d] : rule){
ok &= paint(p, d);
}
if (!ok)
cout << "No" << '\n';
else{
cout << "Yes" << '\n';
if (k == 0)
col[0] = 1;
for(auto &i : col)
cout << i;
cout << '\n';
}
return 0;
}


F - Square Subsequence (abc299 f)

题目大意

给定一个字符串s,问有多少个串 t,满足 tts的一个子序列。

解题思路

首先不考虑拼接,即问字符串s中本质不同的子序列数量。这个难点在于如何不算重。一个方法就是规定一种子序列映射到字符串的方式。

容易想到的就是最近匹配,就是判断字符串t是不是字符串 s的子序列时,对依次对每个 ti进行最近的匹配,能匹配sj就匹配上 。我们就按照这个方式去计算,每个本质不同的子序列就只算到一次。

即设 dp[i]表示以 i结尾的本质不同的子序列数量,设 pos表示最大的 j满足 j<ispos==si,那么 dp[i]=j=posidp[j]

同样,我们可以按照此方式解决算重问题。从上述问题转到这个问题,一个自然的想法是设dp[i][j]表示串 tt中,前一个 t的末尾在 i,后一个 t的末尾在 j(显然有 si==sj)的子串数量。

为方便叙述,设 ttt1t2 ,即T10T11..T1nT20T21...T2nnxt(i,j)表示 si后第一个 字符是 j的位置。

考虑初始状态,如果我们枚举第一个字母是k的话,那么 i=nxt(0,k),而 j的话感觉难以确定,它可以在任意的 aj=k处,区别可能是串 t的长度不同。因此我们得枚举j的位置。

确定了初始状态 dp[i][j]=1后,然后就枚举下一个字母 k,由于采取的是最近匹配的原则,那么下一个匹配位置就分别是 nxt(i,k)nxt(j,k),即 dp[nxt(i,k)][nxt(j,k)]+=dp[i][j],转移式子即为此。

最后累计答案时,因为采取最近匹配的原则,我们只对满足 nxt(x,si)=jdp[x][y]yj即可)进行累加。

总的来说,就是设dp[i][j][k]表示 t2开头在 sit1末尾在sjt2 末尾在sk的方案数。

转移式子 dp[i][nxt(j,c)][nxt(k,c)]+=dp[i][j][k]

答案ans=i=1nnxt(j,si)==ik=indp[i][j][k]

总的时间复杂度是 O(n3)

神奇的代码
#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);
string s;
cin >> s;
int n = s.size();
vector<array<int, 26>> nxt(n);
array<int, 26> pos;
pos.fill(-1);
for(int i = n - 1; i >= 0; -- i){
nxt[i] = pos;
pos[s[i] - 'a'] = i;
}
int ans = 0;
for(int st = 1; st < n; ++ st){
vector<vector<int>> dp(n, vector<int>(n, 0));
int first = s[st] - 'a';
int l = pos[first], r = st;
dp[l][r] = 1;
for(int i = l; i < r; ++ i)
for(int j = r; j < n; ++ j){
for(int k = 0; k < 26; ++ k){
int nl = nxt[i][k], nr = nxt[j][k];
if (nl == -1 || nr == -1 || nl >= r)
continue;
dp[nl][nr] += dp[i][j];
if (dp[nl][nr] >= mo)
dp[nl][nr] -= mo;
}
}
for(int i = l; i < r; ++ i)
for(int j = r; j < n; ++ j){
if (nxt[i][first] == r){
ans += dp[i][j];
if (ans >= mo)
ans -= mo;
}
}
}
cout << ans << '\n';
return 0;
}


G - Minimum Permutation (abc299 g)

题目大意

给定一个长度为n,仅包含数字 1m的数组a,问其字典序最小的一个子序列,是一个排序。

解题思路

从左到右依次考虑数组a,对于当前的数字ai,一个朴素的想法是,选不选它,如果不选它,剩下的序列还能不能组成一个排列,如果不能,则一定要选它,那问题就剩下如何判断能不能组成,以及如果不一定选择,该怎么办。

能不能组成一个排列,就是看剩下序列的数字包不包含还没选择的数,设还没选择的数为left

然后对于不是一定要选的数,我们可以先存起来,这样就有一个由满足要求的ai组成的候选集合。

继续往右遍历,会遇到第一个不满足要求的位置ar,此时可以从候选集合里选数字最小的数,放到答案里,此时left就少了一个数。

因为left是判断某个数是不是一定要选的条件,当left少一时,说明这个条件变得更容易满足,因此ar可能会满足,因此可以继续往右边遍历,往候选集合里添加新的数。而之前满足要求的还是满足。

由此就循环就可以得到最终答案了。

神奇的代码
#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> a(n);
vector<int> cnt(m + 1);
int ok = 0;
auto add = [&](int x, int val){
if (cnt[x] == 0)
ok ++;
cnt[x] += val;
};
auto sub = [&](int x, int val){
cnt[x] -= val;
if (cnt[x] == 0)
ok --;
};
for(auto &i : a){
cin >> i;
add(i, 1);
}
vector<int> ans;
vector<int> used(m + 1, 0);
priority_queue<array<int, 2>> candidate;
int target = m;
int l = -1;
for(int i = 0; i < n; ++ i){
if (used[a[i]])
continue;
if (ok != target){
while(-candidate.top()[1] < l || used[-candidate.top()[0]])
candidate.pop();
int val = -candidate.top()[0];
int pos = -candidate.top()[1];
ans.push_back(val);
used[val] = 1;
l = pos;
if (cnt[val])
sub(val, cnt[val]);
candidate.pop();
-- i;
-- target;
continue;
}
sub(a[i], 1);
candidate.push({-a[i], -i});
}
while(ans.size() < m){
int val = -candidate.top()[0];
int pos = -candidate.top()[1];
candidate.pop();
if (pos < l || used[val])
continue;
ans.push_back(val);
used[val] = 1;
l = pos;
}
for(int i = 0; i < m; ++ i){
cout << ans[i] << ' ';
}
cout << '\n';
return 0;
}

赛后发现是道原题,去年的时候做过。

当时的思路更朴素,首先把第一个数a0放入答案末尾,然后依次考虑之后的每个数ai和答案的末尾的数 ansback比较,如果 ai<ansback,且之后还有一个 aj(j>i)==ansback的话,那么当前的 ansback可以扔掉(仍能保证后续能构造出一个排列),一直扔掉直到ai>ansback或者不存在 aj(j>i)==ansback,此时把 ai放到答案末尾。

然后依次考虑就可以了。时间复杂度是O(n)的。

简短的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 8;
int a[N], cnt[N];
bool used[N];
int ans[N], cur;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> m >> n;
for(int i = 1; i <= m; ++ i){
cin >> a[i];
cnt[a[i]] ++;
}
cur = 0;
for(int i = 1; i <= m; ++ i){
cnt[a[i]] --;
if (used[a[i]])
continue;
while (cur > 0 && cnt[ans[cur]] && ans[cur] >= a[i]){
used[ans[cur]] = false;
-- cur;
}
++ cur;
ans[cur] = a[i];
used[a[i]] = true;
}
for(int i = 1; i <= n; ++ i)
cout << ans[i] << " \n"[i == n];
return 0;
}


Ex - Dice Sum Infinity (abc299 h)

题目大意

<++>

解题思路

<++>

神奇的代码


本文作者:~Lanly~

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

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

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