目录
比赛链接:
A. Channel
题目大意:Petya知道总共有n个人订阅了他的频道,起初有a个人在线(我们规定只要Petya的订阅者在线,就读过Petya的帖子),Petya收到了q个消息,每个消息用'+'代表随机一个订阅者上线,用'-'代表随机一个订阅者下线,根据这q个消息判断是以下三种情况中的哪一种
- 这n个用户不可能都阅读了帖子
- 有可能这n个用户都阅读了帖子
- 这n个用户保证都阅读了帖子
思路:可以先将a个在线的人编入这q个消息中进行判断,当有n个连续的'+'或者经'-'抵消掉部分'+'后的连续'+'数目 >= n时可以保证这n个人都阅读了帖子。我们用cnt来记录这q+a个消息中'+'的出现次数,若cnt >= n则代表可能这n个用户阅读了帖子,否则这n个用户不可能都阅读了帖子
AC代码:
typedef vector<int> vi;
#define FOR(i, a, b) for (int i = a; i < (b); i++)
#define sz(x) (int)(x).size()
void solve() {
int i, j, k, n, a, q;
cin >> n >> a >> q;
string s;
cin >> s;
while (a--) s = "+" + s;
int res, cnt;
res = cnt = 0;
bool flag = false;
FOR(i, 0, sz(s)) {
int count = 0;
if (s[i] == '+') {
while (s[i] == '+') {
res++;
count++;
cnt++;
i++;
}
i--;
} else res--;
if (count == n || res >= n) flag = true;
}
if (flag) cout << "YES" << endl;
else if (cnt >= n) cout << "MAYBE" << endl;
else cout << "NO" << endl;
}
B. Split Sort
题目大意:给你个长度为n的排列p,你可以执行一种操作:选择一个x(2 <= x <= n),令排列中所有比x小的数均按所给排列顺序不变的写在x的前面,所有比x大的数(包括x)均按所给排列的顺序不变的接上前者,然后将此排列替换为原先的排列。问若要使排列递增序,最小的操作次数是多少
思路:通过观察不难发现,如果要使排列递增序,那么需要选择排列中所有没有按排列递增序的数进行操作,也就是将此排列分成n个段,每个段严格遵守a[i] + 1 = a[i + 1],因为每个排列必然会存在一个段为数字1开头,所以这一段根本不用进行交换操作,所以最终的答案即为这n - 1。
以样例5为例:6 4 3 5 2 1
我们可以分为8个段
1 2 3
4
5 6
7 8 9
10 11 12 13
14 15 16
17 18
19
因为1 2 3的段本身已经按排列递增排序,所以我们只需要分别选择数字:4,5,7,10,14,17,19这七位数即可完成最少操作次数
实现上可以利用set,如果set中含有当前数减一的数就进行替换,否则就插入set中,最终的set.size() - 1即为最小操作次数
AC代码:
typedef vector<int> vi;
#define FOR(i, a, b) for (int i = a; i < (b); i++)
#define sz(x) (int)(x).size()
#define lb lower_bound
void solve() {
int i, j, k, n;
cin >> n;
vi nums(n, 0);
set<int>st;
FOR(i, 0, n) {
cin >> nums[i];
if (st.count(nums[i] - 1)) {
st.erase(st.lb(nums[i] - 1));
st.insert(nums[i]);
} else st.insert(nums[i]);
}
cout << sz(st) - 1 << endl;
}
C. MEX Repetition
题目大意:给你长度为n的数组,数组中均为范围为0 ~ n的两两不相同的整数,可以进行一个操作:令数组中的每个下标i(1 <= i <= n)依次替换为MEX(a1,a2,…,an)。其中MEX(a1,a2,…,an)为数组中未出现的最小的非负整数,求进行k次这样的操作后的数组并打印
思路:总共出现了0 ~ n中的所有数,我们可以先遍历一遍数组看哪个数没出现过,然后将这个数加入到这个数组的头部,以样例0,1 ,3进行举例
遍历数组未出现2,将其加入数组头即为2,0,1,3,通过枚举不难看出,k = 1时数组变为2,0,1,k = 2时数组变为3,2,0,k = 3时数组变为1,3,2,k = 4时数组变为0,1,3,k = 5时数组变为2,0,1,这就发现一个规律,答案是进行循环的,所以我们可以对k进行适当取模即可进行模拟了
AC代码:
typedef vector<int> vi;
#define FOR(i, a, b) for (int i = a; i < (b); i++)
#define sz(x) (int)(x).size()
void solve() {
int i, j, k, n;
cin >> n >> k;
vi nums(n + 1, 0);
map<int, int>mp;
set<int>st;
FOR(i, 1, n + 1) {
int x;
cin >> x;
nums[i] = x;
mp[x] = i;
st.insert(x);
}
FOR(i, 0, n + 1) {
if (!st.count(i)) nums[0] = i, mp[i] = 0;
}
vi item(nums);
reverse(item.begin() + 1, item.end());
int cnt = k % sz(nums);
if (cnt == 0) cnt = n;
else cnt--;
cnt = mp[item[cnt]];
while (n--) {
cout << nums[cnt] << " ";
cnt++;
if (cnt == sz(nums)) cnt = 0;
}
cout << endl;
}