CF Round 771 Div2 题解
A题 Reverse
给定一个长度为 \(n\) 的排列,我们可以选择将其中一个区间反转,问怎样反转可以使得这个新排列的字典序最小,并输出这个最小的字典序。
\(1\leq n \leq 500\)
如果字典序已经最小,那么就不管他。
假设区间 \([1,l-1]\) 上面的数都在合适的位置上,而 \(p_l\not= l\),那么我们直接找到后面等于 l 的位置,下标记为 r,然后反转区间 \([l,r]\) 即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 510;
int n, p[N];
void solve() {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> p[i];
for (int i = 1; i <= n; ++i)
if (p[i] != i) {
int l = i, r;
for (int j = n; j > i; j--)
if (p[j] == i) { r = j; break; }
reverse(p + l, p + r + 1);
break;
}
for (int j = 1; j <= n; ++j)
cout << p[j] << " ";
cout << endl;
}
int main()
{
int T;
cin >> T;
while (T--) solve();
return 0;
}
B题 Odd Swap Sort (构造)
给定一个长度为 \(n\) 的数列 \(\{a_n\}\)。如果两个相邻元素之和为奇数,那么我们就可以将其进行交换。问我们能否通过若干次操作将这个数列变为单调不降?
\(1\leq n \leq 10^5,1\leq a_i \leq 10^9\)
一个不是很直观的冷知识:如果两个数都是奇数(偶数),那么他们的相对位置不会产生变化(他俩不可能交换顺序)。
所以我们直接按照奇偶,按顺序将原数组分成两个,如果两个数组均单调不降,那么最后这个数组也是可以通过交换而变为单调不降的。
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> vec[2];
bool solve() {
vec[0].clear(), vec[1].clear();
cin >> n;
for (int i = 1; i <= n; ++i) {
int x;
cin >> x;
vec[x % 2].push_back(x);
}
for (int x = 0; x < 2; ++x) {
vector<int> &v = vec[x];
for (int i = 1; i < v.size(); ++i)
if (v[i - 1] > v[i]) return false;
}
return true;
}
int main()
{
int T;
cin >> T;
while (T--) puts(solve() ? "Yes" : "No");
return 0;
}
C题 Inversion Graph(图,数学,数据结构)
给定一个长度为 \(n\) 的排列 \(\{p_n\}\),我们将依照此排列来建立一个图:
图起初有 \(n\) 个孤立点,倘若存在逆序对 \((i,j)\),则在这两个点之间连一条边。
图建立完毕后,问这个图的连通块的数量是多少。
\(n\leq 10^5\)
有一个并不显然的性质:倘若存在逆序对 \([l,r]\),那么整个区间内的点都在一个连通块内。
证明:我们记 \(p_l=x,p_r=y,p_i=z(l<i<r)\),那么:
- \(z<x\) 时,有 \(p_l>p_i,l<i\),i 和 l 之间连边
- \(z>x\) 时,有 \(p_i>p_r,i<r\),i 和 r 之间连边
又因为 l 和 r 之间存在边,所以整个区间 \([l,r]\) 都在一个连通块内。
那么,我们将最后的连通块按照形成顺序排序,那么他们显然是一个升序,这一流程类似单调栈,所以我们用栈来维护。
我们记栈顶为 \(x\)(空则为 0),新点的值为 \(y\),如果 \(y>x\) 则直接推进去,反之则一直从栈顶 pop,直到栈顶元素小于 \(y\),然后将 \(x\) 推进去。
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, a[N];
int solve() {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
stack<int> s;
for (int i = 1; i <= n; ++i) {
int x = s.empty() ? 0 : s.top();
while (!s.empty() && s.top() > a[i]) s.pop();
s.push(max(a[i], x));
}
return s.size();
}
int main()
{
int T;
cin >> T;
while (T--) cout << solve() << endl;
return 0;
}
D题 Big Brush(BFS,构造)
给定一张 \(n*m\) 的格子图,现在我们可以进行若干次操作,每次操作选定一个坐标 \((x,y)\) 和一个颜色 \(c\),然后将 \((x,y),(x+1,y),(x,y+1),(x+1,y+1)\) 全部涂上 \(c\)。
现在给定一张已经涂好色的地图,问我们能否构造出我们的操作顺序(至多进行 \(nm\) 次操作)?不能则输出 -1。
\(2\leq n,m\leq 10^3,1\leq a_i\leq nm\)
我们首先将地图上可以直接进行操作的那几块进行操作,然后将他们标记上 0。
随后,我们地图上不停找点,如果这个点可进行操作(且有必要进行操作),那么下一次就对他进行操作。
暴力寻找是不可取的(电脑不像人眼一样能一眼看出来),但是每次将一个块操作完之后,我们只考虑其周围的八个点,并考虑下一步对他们进行操作。这个 FIFO 的模式,我们肯定写一个队列来处理。
考虑到操作的次数限制,加上避免死循环,所以额外开一个 vis 数组来标记,避免某个点重复进入队列。
总复杂度 \(O(nm)\),不过自带大常数,需要花心思优化。
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m, a[N][N], vis[N][N];
inline bool can(int i, int j) { return i >= 1 && i < n && j >= 1 && j < m; }
int check1(int x, int y) {
return a[x][y] == a[x][y + 1] && a[x][y] == a[x + 1][y] && a[x][y] == a[x + 1][y + 1];
}
int check2(int x, int y) {
if (!can(x, y)) return 0;
set<int> s;
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
if (a[x + i][y + j])
s.insert(a[x + i][y + j]);
return s.size() == 1 ? *s.begin() : 0;
}
void update(int x, int y) {
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
a[x + i][y + j] = 0;
}
struct Node { int x, y, c; };
queue<Node> q;
stack<Node> ans;
int main()
{
//read
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> a[i][j];
//solve
for (int i = 1; i < n; ++i)
for (int j = 1; j < m; ++j)
if (check1(i, j)) {
vis[i][j] = 1;
Node t = (Node){i, j, a[i][j]};
q.push(t), ans.push(t);
}
while (!q.empty()) {
int x = q.front().x, y = q.front().y;
q.pop();
update(x, y);
for (int i = -1; i <= 1; ++i)
for (int j = -1; j <= 1; ++j) {
int v = check2(x + i, y + j);
if (v && !vis[x + i][y + j]) {
vis[x + i][y + j] = 1;
Node t = (Node){x + i, y + j, v};
q.push(t), ans.push(t);
}
}
}
//output
int res = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (!a[i][j]) ++res;
if (res != n * m) puts("-1");
else {
cout << ans.size() << endl;
while (!ans.empty()) {
Node t = ans.top();
printf("%d %d %d\n", t.x, t.y, t.c);
ans.pop();
}
}
return 0;
}