AtCoder Beginner Contest 260 A-G
AtCoder Beginner Contest 260
https://atcoder.jp/contests/abc260/tasks
A - A Unique Letter
题意
给定一个字符串,输出任意一个只出现了一次的字符
分析
直接模拟啦
Code
#include <bits/stdc++.h>
using namespace std;
int cnt[27];
int main () {
string s;
cin >> s;
for (int i = 0; i < 3; i ++)
cnt[s[i] - 'a'] ++;
for (int i = 0; i < 26; i ++) {
if (cnt[i] == 1) {
cout << (char)(i + 'a');
return 0;
}
}
cout << -1;
}
B - Better Students Are Needed!
题意
现有n人,录取规则为:
数学前x名 -> 英语前y名 -> 数学加英语前z名
同分则按照编号前的先录取
按编号升序输出录取的人
分析
结构体排序,模拟即可
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n, x, y, z;
bool vis[N];
//int a[N], b[N]; //math,eng
struct Node {
int id, a, b;
}e[N];
bool cmp1 (Node x, Node y) {
if (x.a == y.a) return x.id < y.id;
return x.a > y.a;
}
bool cmp2 (Node x, Node y) {
if (x.b == y.b) return x.id < y.id;
return x.b > y.b;
}
bool cmp3 (Node x, Node y) {
if (x.b + x.a == y.b + y.a) return x.id < y.id;
return x.b + x.a > y.b + y.a;
}
int main () {
cin >> n >> x >> y >> z;
vector <int> v;
for (int i = 1; i <= n; i ++) cin >> e[i].a, e[i].id = i;
for (int i = 1; i <= n; i ++) cin >> e[i].b;
sort (e + 1, e + n + 1, cmp1);
for (int i = 1; i <= x; i ++) {
v.push_back (e[i].id);
//cout << e[i].id << endl;
vis[e[i].id] = true;
}
sort (e + 1, e + n + 1, cmp2);
for (int i = 1; i <= y; i ++) {
if (vis[e[i].id]) {
y ++;
continue;
}
v.push_back (e[i].id);
//cout << e[i].id << endl;
vis[e[i].id] = true;
}
sort (e + 1, e + n + 1, cmp3);
for (int i = 1; i <= z; i ++) {
if (vis[e[i].id]) {
z ++;
continue;
}
v.push_back (e[i].id);
//cout << e[i].id << endl;
vis[e[i].id] = true;
}
sort (v.begin(), v.end());
for (auto i : v) cout << i << endl;
}
//数学前x名,英语前y名,数学加英语前z名
//同分则按照编号前的先录取
//输出录取的人
C - Changing Jewels
题意
现有红蓝两种宝石,每种宝石都有分不同的level
最开始给定一个level为n的红宝石,可以进行如下转化:
(两种操作)(把level位n的红宝石简称为红n)
- 把1个红n变成: 1个红n-1 + X个蓝n
- 把1个蓝n变成: 1个红n-1 + Y个蓝n-1
问最多能有多少个蓝1
分析
模拟递推式即可,注意要先处理2.再处理1.,因为蓝n可以在1.中产生,所以要先求出他的值
表示为 1 个level为 i 的蓝宝石能够产生多少个level为1的蓝宝石,
表示为 1 个level为 i 的红宝石能够产生多少个level为1的蓝宝石。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
int r[15], b[15];
signed main () {
int n, x, y;
cin >> n >> x >> y;
if (n < 2) {
cout << 0;
return 0;
}
r[1] = 0, b[1] = 1;
for (int i = 2; i <= n; i ++) {
b[i] = r[i-1] + b[i-1] * y; //先更新他,因为后面要按用到b[i]
r[i] = r[i-1] + b[i] * x;
}
cout << r[n] << endl;
}
//两种操作
//1. 把1个红n变成: 1个红n-1 + X个蓝n
//2. 把1个蓝n变成: 1个红n-1 + Y个蓝n-1
//问最多能有多少个蓝1
//好小的范围
//蓝的里面还有红的
//递推,预处理
//模拟递推式即可
D - Draw Your Cards
题意
现有n张牌叠在一起,每次从最上面取出一张牌(上面的值为x),面朝上放在桌上,放置规则为:
- 找到已经在桌上的牌中第一个>=x的,叠在上面(更新该处的值)
- 若每日找到符合要求的,则新开一堆(单张牌放着)
如果某一堆摊开的牌个数达到了k,就可以把这一堆拿走
按照牌序号1-n的顺序输出该牌是什么时候被拿走的,没被拿走的牌则输出-1
分析
模拟。set 维护每一堆牌的数量以及顶部的那张牌的值
二分查找值来实现更新
有一个不太好处理的点,就是在拿走k张牌的时候,如何找到路径呢?
那么可以用一个数组来迭代保存路径(有点像链表一样,往回找)
然后因为取牌是有顺序的,所以可以在线处理
注意:要用set自带的lower_bound,效率会高很多很多
效率差距:
Code
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int cnt[N], nxt[N]; //数量 路径
int main () {
int n, k;
cin >> n >> k;
vector <int> ans (n+1, -1);
set <int> table; //维护桌上的最大值
for (int i = 1; i <= n; i ++) {
int x; cin >> x;
if (k == 1) {
ans[x] = i;
continue;
}
auto pos = table.lower_bound (x);
//cout << "pos=" << *pos << endl;
//开一叠新的
if (pos == table.end()) {
table.insert (x);
cnt[x] ++;
}
//叠上+更新值
else {
nxt[x] = *pos;
cnt[x] = cnt[*pos] + 1;
table.erase (pos), table.insert (x);
}
//能被拿走
if (cnt[x] == k) {
table.erase(x);
int tk = k, tx = x;
while (tk --) {
ans[tx] = i;
tx = nxt[tx];
}
}
}
for (int i = 1; i <= n; i ++) cout << ans[i] << endl;
}
//很像蜘蛛纸牌
// 把最上面的牌拿出来,上面的值为x
// 找到已经翻开的牌中第一个>=x的,叠在上面(更新该处的值)
// 如果某一堆摊开的牌个数达到了k
// 1-n输出该牌是什么时候被吃掉的,没被吃掉则输出-1
// 模拟+二分查找
E - At Least One
题意
给定 n 个数对 ,对于一段连续区间 , 若对于所有数对 满足
或 ,则该区间是 好区间
问长度为1,2,3,...,m 的好区间分别有多少个
分析
易得出一条性质:如果 是一个好区间,那么所有包含 的区间都是好区间,所以只需求出一个最小的好区间 ,然后就能根据差分等算出区间个数了
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int cnt[N], ans[N];
int main () {
int n, m;
cin >> n >> m;
vector <int> pos[m+1];
for (int i = 1; i <= n; i ++) {
int a, b;
cin >> a >> b;
pos[a].push_back (i), pos[b].push_back (i);
}
int res = n;
for (int l = 1, r = 1; l <= m; l ++) {
//滑动窗口
//固定l,找到满足条件的最小右边界r
while (r <= m && res) {
for (auto i : pos[r]) {
if (cnt[i] == 0) res --;
cnt[i] ++;
}
r ++;
}
if (res) break; //再往后的都不满足要求了
ans[r-l] ++, ans[m+1-l+1] --; //[l,r]满足条件,则长度在[r-l+1, m-l+1]的都满足
//l右移,消除原本贡献
for (auto i : pos[l]) {
cnt[i] --;
if (cnt[i] == 0) res ++;
}
}
for (int i = 1; i <= m; i ++) {
ans[i] += ans[i-1];
cout << ans[i] << ' ';
}
}
//差分+双指针
F - Find 4-cycle
题意
给定一个无向图,两个独立集(集合内部的点没有被边连接S,T ,有 [m 条无向边,请找到一个四元环(有四个点的环),若没有,输出 -1
分析
四元环必有两个点来自S,两个点来自T(因为独立集的性质保证了集合内部没有边相连),所以一条边的两个端点一定来自不同集合,不难推出,四元环必然是"一点在S,一点在T,一点在S,一点在T"这样交替出现的形式
观察发现 T 小,所以可以枚举S中的点a ,并枚举与其相连的两个(在T中的)的点b,d, 那么当且仅当 S中存在一点c,与b,d相连时,四元环存在
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 5, M = 5e5 + 5;
vector <int> T[M]; //T集
int f[N][N];
int main () {
int n, m, k;
cin >> n >> m >> k;
while (k --) {
int x, y;
cin >> x >> y;
T[x].push_back (y-n);
}
for (int i = 1; i <= n; i ++) {
for (auto x : T[i])
for (auto y : T[i]) {
if (x == y) continue;
if (!f[x][y]) f[x][y] = i;
else {
cout << i << ' ' << x+n << ' ' << f[x][y] << ' ' << y+n << endl;
return 0;
}
}
}
cout << "-1\n";
}
G - Scalene Triangle Area
题意
给定大小为 的 矩阵,若某点 的为,其覆盖范围为:满足以下条件的所有位置
- + < M
q次询问,对于每次询问,要求给出对应位置被覆盖了多少次
分析
一开始不是很懂,看这个理解的
一些类似差分的思想,假如只有最左上角是O,那么覆盖状况如表1所示:
(由覆盖的性质易得,单点所形成的被覆盖的图形会呈现 三角形 的样子)
(图源自上面的链接)
表2是对于每一行进行求和,+号表示求和开始的位置,-号表示求和结束的位置;
同理,表3是对竖行进行求和(即把每行的累加过来)
表4是感觉看不出来啥用(标记起点终点?)
然后按行列递推求即可:
行:(直接加到上一个)add[i][j] += add[i][j - 1], del[i][j] += del[i][j - 1]
列:(呈阶梯状)add[i][j] += add[i - 1][j], del[i][j] += del[i - 1][j + 2]
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2005;
int add[N][5*N], del[N][5*N];
string s[N];
int main() {
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++) {
cin >> s[i];
for(int j = 1; j <= n; j++)
if(s[i][j-1] == 'O') {
++ add[i][j], -- del[i][j + 2 * m]; //横
-- add[min(i+m, n+1)][j], ++ del[min(i+m, n+1)][j]; //纵
}
}
//纵
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n + 2 * m; j++)
add[i][j] += add[i-1][j], del[i][j] += del[i-1][j+2];
//横
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
add[i][j] += add[i][j-1], del[i][j] += del[i][j-1];
int q;
cin >> q;
while(q --) {
int x, y;
cin >> x >> y;
cout << add[x][y] + del[x][y] << endl;
}
}
// 给定大小为N*N的OX矩阵,若矩阵的(s,t)处为O,其覆盖范围为:满足以下条件的所有位置(i,j)
// s <= i && t <= j
// (i - s) + (j - t) / 2 < M
// 再给出Q次询问,对于每次询问(x,y),要求给出对应位置被覆盖了多少次。
//纵向横向累加,维护+表和-表