Codeforces Round #807 (Div. 2)
这场上了80分,刚好上蓝嘿嘿。
传送门:https://codeforces.com/contest/1705
A. Mark the Photographer
题意:2n个人排成两排,每排n个人。要求后面的人要比前面的人高至少x
问是否可以。
解:直接升序排序,第i矮的站在第i+n矮的前面是最优的。
#include<bits/stdc++.h>
#define int long long
const int mo = 998244353;
const int N = 1e6;
signed main() {
std::ios::sync_with_stdio(false);
int t; std::cin >> t;
while (t--) {
int n, x; std::cin >> n >> x;
int h[210] = { 0 };
for (int i = 1; i <= 2 * n; i++)std::cin >> h[i];
std::sort(h + 1, h + 1 + 2 * n);
bool f = 1;
for (int i = 1; i <= n; i++) {
if (h[i + n] - h[i] < x)f = 0;
}
if (f)std::cout << "YES" << std::endl;
else std::cout << "NO" << std::endl;
}
}
B. Mark the Dust Sweeper
题意:有一个数组a,有一种操作:
每次选择一个i,j。要求i<j并且\(a_i\)到\(a_{j-1}\) 都大于0。
然后\(a_i\)-1,\(a_j\)+1。
问最少多少步使得\(a_1\)到\(a_{n-1}\)全为0
解法:
如果一段正整数连续,那么最前面的数字可以直接移动到最后面。
如果中间被0断开,可以花最小的代价,用1在这些0上铺出一条路。
比如 :0 3 0 0 0 0 1 3
可以先铺成:0 1 1 1 0 0 1 3
然后这段连续的1无法再铺了,那么就让它蠕动前进。也就是第一个1铺到最后一个1后面:0 0 1 1 1 0 1 3
然后就变成了: 0 0 0 1 1 1 1 3
此时就连通了。答案就是之前铺路的损耗加上现在每个数字直接到达\(a_n\)的代价。
#include<bits/stdc++.h>
#define int long long
const int mo = 998244353;
const int N = 1e6;
int a[N];
signed main() {
std::ios::sync_with_stdio(false);
int t; std::cin >> t;
while (t--) {
int n; std::cin >> n;
for (int i = 1; i <= n; i++)std::cin >> a[i];
int ans = 0;
int p = 1;
for (int i = 1; i < n; i++) {
while (!a[p] && p <= n)p++;
if (p > i)i = p;
if (i >= n)break;
if (a[i] == 0) {
a[p]--;
a[i] = 1;
ans++;
}
}
for (int i = 1; i < n; i++)ans += a[i];
std::cout << ans << std::endl;
}
}
C. Mark and His Unfinished Essay
题意:给一个字符串,有c次操作,q次询问。每次操作都把字符串的[l,r]区间复制粘贴到字符串末尾,字符串更新。每次询问输出字符串第k位的字符是什么。
思路:显然暴力模拟,空间是开不下的,因为都肥到1e18了。考虑到操作次数不多,只有40,可以记录每次操作,然后对每次询问都遍历操作进行模拟。(一开始没发现只有40次操作,撒呼呼写了二分,其实不二分也可以的)
比如询问k,我们可以去寻找k在哪次操作之后的区间。找到这个操作。那么相当于就是问k-这次操作之前的长度+这次操作的起点-1位置的字符。
然后就循环向回跑,直到跑到第一次操作之前。此时就可以直接得到答案了
#include<bits/stdc++.h>
#define int long long
const int mo = 998244353;
const int N = 1e6;
int a[N];
signed main() {
std::ios::sync_with_stdio(false);
int t; std::cin >> t;
while (t--) {
int n; std::cin >> n;
int c, q; std::cin >> c >> q;
std::string s; std::cin >> s;
std::vector<std::pair<int, int>>ve;
ve.push_back({ 0,0 });
ve.push_back({ 1,n });
std::vector<int>h;
h.push_back(0);
h.push_back(n);
int lasth = n;
while (c--) {
int a, b; std::cin >> a >> b;
ve.push_back({ a,b });
lasth += b - a + 1;
h.push_back(lasth);
}
while (q--) {
int k; std::cin >> k;
int p = std::lower_bound(h.begin(), h.end(), k) - h.begin();
int l = k - h[p - 1] + ve[p].first -1;
while (p != 1) {
//std::cout << "p="<<p << "\n";
p = std::lower_bound(h.begin(), h.end(), l) - h.begin();
//std::cout << "l=" <<l<< std::endl; std::cout << "p=" << p << "\n";
l = l - h[p - 1] + ve[p].first -1;
}
std::cout<< s[l-1] << "\n";
}
}
}
D. Mark and Lightbulbs
题意:有2个字符串,要从第一个字符串s变到第二个字符串t。求最小操作次数,如果不可能则输出-1。
操作:选择一个位置i,要求s[i-1]与s[i+1]不同,i属于[2,n-1] (从1开始储存)。翻转s[i]。
也就是说:011可以变成 001 ,010不可操作。
解法:
不难发现 对一个连续的1区间: 00001110
可以变成:00011110,也可以变成 00000110
也就是说,每一个连续的1区间可以伸缩蠕动,而且不管怎么蠕动,区间数不会变少也不会变多。
于是就好解决了:把两个字符串都拆成连续的1区间。
如果区间数不同则输出-1。
如果相同,则遍历所有连续1的区间,t的第一个目标区间一定是由s的第一个区间蠕动得到的,操作次数就是区间头尾对应差的绝对值之和了。
注意,由于1和n的位置无法翻转,所以无法蠕动到,如果不同一定是不可能,输出-1
#include<bits/stdc++.h>
#define int long long
const int mo = 998244353;
const int N = 1e6;
int a[N];
std::string s, t;
std::vector<std::pair<int, int>>vs,vt;
signed main() {
std::ios::sync_with_stdio(false);
int q; std::cin >> q;
while (q--) {
int n; std::cin >> n;
vs.clear(); vt.clear();
std::cin >> s >> t;
if (s == t) {
std::cout << 0 << "\n";
continue;
}
if (s[0] != t[0] || s[n - 1] != t[n - 1]) {
std::cout << -1 << "\n";
continue;
}
int l = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '1' && (!i || s[i - 1] == '0'))l = i;
if (s[i] == '1' && (i == n - 1 || s[i + 1] == '0')) {
vs.push_back({ l,i });
}
}
l = 0;
for (int i = 0; i < n; i++) {
if (t[i] == '1' && (!i || t[i - 1] == '0'))l = i;
if (t[i] == '1' && (i == n - 1 || t[i + 1] == '0')) {
vt.push_back({ l,i });
}
}
if (vt.size() != vs.size()) {
std::cout << -1 << "\n";
continue;
}
int ans = 0;
//bool f = 1;
//int last = -2;
for (int i = 0; i < vs.size(); i++) {
//if (last + 1 >= vs[i].first)f = 0;
ans += std::abs(vs[i].first - vt[i].first) + std::abs(vs[i].second - vt[i].second);
//last = vt[i].second;
}
//if (!f) std::cout << -1 << "\n";
//else
std::cout << ans << "\n";
}
}
E. Mark and Professor Koro
看成2进制进位。线段树操作。
传送门:https://codeforces.com/contest/1705/problem/E
wa3
#include<bits/stdc++.h>
#define int long long
const int N = 1e6;
int a[N];
int mp[N];
signed main() {
std::ios::sync_with_stdio(false);
int n, q; std::cin >> n >> q;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
mp[a[i]]++;
}
int ans = 0;
for (int i = 1; i <= 3e5; i++) {
mp[i + 1] += mp[i] / 2;
if (mp[i + 1])ans = i + 1;
}
while (q--) {
//ans = 0;
int k, l;
std::cin >> k >> l;
mp[a[k]]--;
int p = a[k];
while (mp[p] % 2 == 1) {
if (mp[p + 1] == 0)break;
mp[p + 1]--;
if (mp[p + 1] > 0)ans = p + 1;
p++;
}
mp[l]++;
p = l;
while (mp[p] % 2 == 0) {
mp[p + 1]++;
ans = std::max(p + 1, ans);
p++;
}
a[k] = l;
if (!mp[ans]) {
for (int i = ans - 1; i >= 1; i--)if (mp[i]) {
ans = i;
break;
}
}
std::cout << ans << "\n";
}
}
浙公网安备 33010602011771号