AtCoder Beginner Contest 299
A - Treasure Chest
题目大意
给定一个由' | ' ' * '和' . '组成的字符串, 并且保证一定有1个' * '和2个' | ', 检查' * '是否在两个' | '之间;
解题思路
签到题不多嗦了;
但是这里可以注意一下string的find函数; find(char c, int pos)意为从第pos个字符开始找字符c, 返回值是int, pos可以不写, 默认从开头开始找; 而这里我们用到了两个拓展的find函数: find_first_of(char c)和find_last_of(char c), 意为字符c第一次出现的位置和最后一次出现的位置;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5+10;
int n, m,idx;
signed main() {
string s;
cin >> n >> s;
auto a = s.find_first_of('|');
auto b = s.find_last_of('|');
auto c = s.find('*');
if (c >= a && c <= b) {
cout << "in";
}
else cout << "out";
return 0;
}
B - Trick Taking
题目大意
有n个人玩游戏, 每个人都有各自的颜色和序号; 现在给定一个颜色m, 如果有人的颜色也是m, 那么赢家就是这些人里面序号最大的; 如果没有人的颜色是m, 那么赢家就是与1号玩家颜色相同的玩家中序号最大的, 注意1号玩家也有可能是赢家
解题思路
签到题不多嗦了;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5+10;
int n, m,idx;
vector<int> v;
int r[N], c[N];
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> c[i];
if (c[i] == m) v.push_back(i);
}
for (int i = 1; i <= n; i++) cin >> r[i];
int maxn = 0;
if (v.size()) {
for (int x : v) {
if (r[x] > r[maxn]) maxn = x;
}
cout << maxn;
}
else {
for (int i = 1; i <= n; i++) {
if (c[i] == c[1]) {
if (r[i] > r[maxn]) maxn = i;
}
}
cout << maxn;
}
}
C - Dango
题目大意
给定一个只由' o '和' - '组成的字符串, 先定义一种字符串s, s的开头或结尾其中一个必须是' - ', 并且s的长度取决于' o '的个数, 例如" oooo- "的长度为4; 现在从给定的字符串里面找到符合字符串s的要求的子串中最长的长度;
解题思路
以' - '为节点作为结算即可; 算是个签到题;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5+10;
int n, m,idx;
signed main() {
cin >> n;
string s;
cin >> s;
int maxn = 0;
bool f = false;
for (int i = 0; i < n; i++) {
if (s[i] == 'o') idx++;
else {
f = true;
maxn = max(maxn, idx);
idx = 0;
}
}
maxn = max(maxn, idx);
if (maxn == 0) f = false;
if (f) cout << maxn;
else cout <<-1;
}
D - Find by Query
题目大意
本题是一个交互题, 现有一个由01组成长度为n的字符串, 并且s1=0, sn=1; 现在可以最多给出20次询问, 输出" ? x ", x是1~n中的一个, 然后会给出sx的值; 现在需要我们找出一个位置p, 满足sp不等于s(p+1);
解题思路
这还是第一次遇到交互题, 看了看题解发现交互题就是你按规定样式输出后, 网站会根据你的输出, 把对应结果输入到缓冲区, 此时我们用直接用cin输入后就可以得到想要的答案;
这个题是一个二分题, 因为开头是0, 结尾是1, 所以我们先询问一个位置x, 如果为1; 则在1~x-1中一定有一个位置p使得sp=0, s(p+1)=1; 因此我们让l作为p, r作为p + 1, 修改一下二分停止的条件, 当r = l + 1时就可以停止了; 因为n是1e5级别的, 所以20次一定能找到最后答案;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10, mod = 998244353;
int n, m, k, res;
signed main(){
cin >> n;
int l = 1, r = n;
while(l + 1 < r){
int mid = l + r + 1>> 1;
cout << "? " << mid << endl;
cin >> m;
if(m == 1) r = mid;
else l = mid;
}
cout << "! " << l;
return 0;
}
E - Nearest Black Vertex
题目大意
给定一个无向图, 有n个点和m条边; 现在我们需要对其中的点进行黑白两个颜色的染色; 规则如下: 一是至少有一个黑点; 二是题目会给出k组限制, 每组限制包括一个点a和一个距离b, 意为距离a最近的黑点与a之间的距离必须为b; 输出形式以01序列表示, 0表示白点, 1表示黑点;
解题思路
因为n只有2000, 所以可以考虑用bfs; 对于每次限制点a和距离d, 我们都可以用一次bfs, 距离小于d的点染成白色, 等于d的的点染成黑色; 规定可以用白色覆盖黑色, 但是不能用黑色覆盖白色; 在染完后对于每个a, 我们都要检查距离他为d的点中至少有一个还是黑色;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
const int N = 2e3 + 10, mod = 998244353;
int n, m, k, res;
vector<int> v[N], ch[N];
vector<int> node;
int f[N], p[N], d[N];
bool st[N];
void bfs(int u){
memset(st, false, sizeof st);
queue<PII> q;
q.push({u, 0});
st[u] = true;
while(q.size()){
PII t = q.front();
q.pop();
int dis = t.second + 1;
for(int x : v[t.first]){
if(st[x]) continue;
st[x] = true;
if(dis == f[u]){
if(!p[x]) p[x] = 1;
ch[u].push_back(x);
}
else if(dis < f[u]){
p[x] = 2;
q.push({x, dis});
}
}
}
}
signed main(){
cin >> n >> m;
for(int i = 1; i <= m; i++){
int a, b;
cin >> a >> b;
v[a].push_back(b);
v[b].push_back(a);
}
cin >> k;
for(int i = 1; i <= k; i++){
int a , b;
cin >> a >> b;
f[a] = b;
node.push_back(a);
}
for(int x : node){
if(f[x] == 0){
if(!p[x]) p[x] = 1;
ch[x].push_back(x);
continue;
}
p[x] = 2;
bfs(x);
}
bool f = true;
for(int x : node){
bool f1 = false;
for(int y : ch[x]){
if(p[y] == 1){
f1 = true;
break;
}
}
if(!f1){
f = false;
break;
}
}
if(f){
cout << "Yes" << endl;
for(int i = 1; i <= n; i++){
if(p[i] == 2) cout << 0;
else cout << 1 ;
}
}
else cout << "No";
return 0;
}
F - Square Subsequence
难度: ⭐⭐⭐⭐⭐
题目大意
给定一个字符串S, 问有多少种字符串T, 使得字符串TT(两个T拼起来)是字符串S的一个子序列; 注意是子序列不是子串, 不要求连续;
解题思路
很难的一道dp; 要做的第一件事是要考虑去重, 样例zzz的输出是1, 只有"z"一种; 为了去重, 对于S种连续相同的字符c, 我们只取当前最靠前的; eg: aabc ddd中字符串ab的下个字符如果是d就只能是第一个d, aabcd dd, 如果字符串abd的下个字符还是d就只能是第二个d; 所以我们可以用一个数组next[i][c]表示从第i个位置往后字符c第一次出现的位置;
做完预处理后开始考虑dp; 状态表示为dp[i][j]表示第一个T的最后一个字符的位置是i,第二个T的最后一个字符的位置是j时有多少种满足要求的字符串; 状态转移可以利用前面预处理中的next数组: dp[next[i][c]][next[j][c]] += dp[i][j]; 转移方程也不难理解, 就是在原本在i和j处结束的T1和T2分别又往后延申到了各自区间中第一个字符c的位置;
因为T2不知道起点,但是T1的起点一定是1, 所以我们可以先遍历T1的终点ed; 则T1的区间就是[1,ed], T2为[ed+1,n]; 然后我们进行初始化, 也就是找T1和T2可能的起点, 也就是T的长度为1的情况; 遍历26个字母i, 如果next[0][i] <= ed并且next[ed][i] <= n说明当前字符i满足, 于是就可以把dp[next[0][i]][next[ed][i]]初始化为1;
随后我们在两个区间中找可以延申的方案, 设i是T1当前最后字符的位置, j是T2; 如果dp[i][j]不为0, 说明存在这样的一个T, 然后我们可以看看是否可以把它延申, 方法和初始化一样, 遍历26个字母, 如果得到的位置都在范围内就进行状态转移即可;
因为我们最外面的循环时遍历T1的结束位置ed, 所以该循环汇总的时候res要加上dp[ed][i], i是T2的结束位置, 遍历ed+1到n即可;
呼~ 太不容易了...
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e3 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, res;
int dp[N][N], nex[N][26];
signed main() {
string s;
cin >> s;
n = s.size();
s = ' ' + s;
for (int i = 0; i < 26; i++) nex[n][i] = n + 1;
for (int i = n - 1; i >= 0; i--) {
for (int j = 0; j < 26; j++) {
nex[i][j] = nex[i + 1][j];
}
nex[i][s[i + 1] - 'a'] = i + 1;
}
for (int ed = 1; ed <= n; ed++) {
memset(dp, 0, sizeof(dp));
for (int i = 0; i < 26; i++) {//找当前区间中所有合法的起点
if (nex[0][i] <= ed && nex[ed][i] <= n)
dp[nex[0][i]][nex[ed][i]] = 1;
}
for (int i = 1; i <= ed; i++) {
for (int j = ed + 1; j <= n; j++) {
if(!dp[i][j]) continue;
for (int k = 0; k < 26; k++) {
int l = nex[i][k], r = nex[j][k];
if (l <= ed && r <= n) {
dp[l][r] = (dp[l][r] + dp[i][j]) % mod;
}
}
}
}
for (int i = ed + 1; i <= n; i++){
res = (res + dp[ed][i]) % mod;
}
}
cout << res;
return 0;
}

浙公网安备 33010602011771号