Codeforces Round 997 (Div. 2)
A. Shape Perimeter
题意:一个\(m \times m\)的印章每次往右上方向移动,问盖出来的图形周长。
假设每次都能盖\(m \times m\)的格子,那么看每两次盖章有多重复的减去就行。第\(i\)次移动了\(x_i, y_i\),那么就会有一个\(m-x_i, m-y_i\)的矩形被重复覆盖,减去它的周长。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> x(n), y(n);
for (int i = 0; i < n; ++ i) {
std::cin >> x[i] >> y[i];
}
i64 ans = m * 4 * n;
for (int i = 1; i < n; ++ i) {
ans -= (m - x[i] + m - y[i]) * 2;
}
std::cout << ans << "\n";
}
B. Find the Permutation
题意:有一个排列,给你一个矩阵表示\(y\)是否在\(x\)后面\((y>x)\), 求出这个排列。
对于每一对\(xy\),我们都可以知道谁在前面,前面的向后面连边,那么没入度的点就是第一个,依次推出其他点的位置。用拓扑排序即可。
因为我们知道每个数有几个在他前面,那么位置也确定了,赛时没想这么多,其实不用拓扑排序。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
std::vector<std::vector<int> > adj(n);
std::vector<int> in(n);
for (int i = 0; i < n; ++ i) {
for (int j = i + 1; j < n; ++ j) {
if (s[i][j] == '1') {
adj[i].push_back(j);
++ in[j];
} else {
adj[j].push_back(i);
++ in[i];
}
}
}
std::queue<int> q;
std::vector<int> ans;
for (int i = 0; i < n; ++ i) {
if (in[i] == 0) {
q.push(i);
}
}
while (q.size()) {
int u = q.front(); q.pop();
ans.push_back(u);
for (auto & v : adj[u]) {
if ( -- in[v] == 0) {
q.push(v);
}
}
}
for (auto & x : ans) {
std::cout << x + 1 << " \n"[x == ans.back()];
}
}
C. Palindromic Subsequences
题意:构造一个数组使得长度最长的最长回文子序列数量最多。
如果我们填\(1\)~\(n-3\),后面三个填1,2,3。你们发现会有\(3(n-4)\)个最长回文子序列。当\(n\)大于6的时候满足\(3(n-4) > n\)。\(n\)等于6我直接抄的样例。
点击查看代码
void solve() {
int n;
std::cin >> n;
if (n == 6) {
std::cout << 1 << " " << 1 << " " << 2 << " " << 3 << " " << 1 << " " << 2 << "\n";
return;
}
int m = n - 3;
for (int i = 1, x = 0; i <= n; ++ i) {
std::cout << x + 1 << " \n"[i == n];
x = (x + 1) % m;
}
}
D. Unique Median
不会。坐了两个小时牢
题意:如果一个数组长度为奇数,或者长度是偶数以及两个中位数相等,就是好的。给你一个数组,求有多少个子数组是好的。
这题我们反着做,求出有多少不好的子数组,然后用总数减去就是答案。
什么样的数组是不好的?恰好有一个\(x\)满足\(1\)到\(x\)的总个数是\(\frac{len}{2}\)。如果让\(a_i <= x\)时,\(b_i = -1\),让\(a_i > x\)时,\(b_i = 1\),那么一个子数组是坏的代表它的和为0。那么我们枚举这个\(x\),看有多少个值为\(0\)的子数组。
设\(sum_i\)为枚举\(x\)时\(\sum_{i=1}^{i} b_i\),那么我们需要减去一个\(sum_j,\)满足\(sum_i - sum_j = 0\),意为所有\([j + 1, i]\)是坏的\(j\)。但要注意有可能区间里没有\(x\),那么不应该算这个区间。比如我们枚举\(x=4\)时,\([1, 2, 6, 6]\)的确是坏的,但后面枚举\(x=5\)时,我们又会算一遍,这个数组应该只在\(x=2\)时算一遍。所以我们要在出现一个\(x\)时再把没加进来的\(sum\)加进来。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
i64 ans = (i64)n * (n + 1) / 2;
for (int x = 1; x <= 10; ++ x) {
std::vector<int> sum(n + 1);
for (int i = 0; i < n; ++ i) {
sum[i + 1] = sum[i] + (a[i] > x ? 1 : -1);
}
std::vector<int> cnt(2 * n + 1);
for (int i = 1, j = 0; i <= n; ++ i) {
if (a[i - 1] == x) {
while (j < i) {
++ cnt[sum[j] + n];
++ j;
}
}
ans -= cnt[sum[i] + n];
}
}
std::cout << ans << "\n";
}