Educational Codeforces Round 102
C. No More Inversions
首先我们先来看对 1 2 3 ... k-1 k k-1 ... 3 2 1 这样的序列进行题目中的变化操作。
k = 2
1 2 1 逆序对1个
2 1 2 逆序对1个
k = 3
1 2 3 2 1 逆序对4个:(2,1) (3,2) (3,1) (2,1)
1 3 2 3 1 逆序对4个:(3,2) (3,1) (2,1) (3,1)
2 1 3 1 2 逆序对4个:(2,1) (2,1) (3,1) (3,2)
3 2 1 2 3 逆序对4个:(3,2) (3,1) (3,2) (2,1)
k = 4
1 2 3 4 3 2 1 逆序对9个:(2,1) (3,2) (3,1) (4,3) (4,2) (4,1) (3,2) (3,1) (2,1)
2 3 1 4 1 3 2 逆序对9个:(2,1) (2,1) (3,1) (3,1) (3,2) (4,1) (4,3) (4,3) (3,2)
所以可以得出结论:形如 1 2 3 ... k-1 k k-1 ... 3 2 1 无论进行怎样的操作,序列的逆序对数量不变
对于原数组 a , 为了方便表示取 n = 7 , a = 5
则 a = 1 2 3 4 5 4 3 , 把 1 2 称为前半段, 把 3 4 5 4 3 称为后半段
可以观察到 a 的逆序对来自后半段。
可以发现的是无论怎样操作序列的后半段始终是一个符合刚才描述的回文串,即无论怎样操作后半段的逆序对数量不变。
因为题目要求操作后的序列b的逆序对数量不超过a的逆序对数量,所以我们不可以在额外增加逆序对是数量,实际上a b 逆序对数量相同
先考虑前半段只要反序则逆序对数量一定增加
再考虑能不能把前半段的某个值换到后半段,即 p = 1 3 2 4 5 , b = 1 3 2 4 5 4 2 这种情况。因为a后半段的值一定大于前半段,所以只要发生这样的操作逆序对的数量一定增加
总结下来就是:能够交换的部分只能是后半部分。
现在考虑题目的要求,要使得 b 的字典序最大,则可以想到让后半部分完全反序,即 b = 1 2 5 4 3 4 5 , 这种情况所以构造的p应当是 p = 1 2 5 4 3 这种后半段完全逆序的情况
#include <bits/stdc++.h>
using namespace std;
#define int long long
using pii = pair<int, int>;
void solve() {
int n, k, t;
cin >> n >> k, t = 2 * k - n;
for (int i = 1; i < t; i++) cout << i << " ";
for (int i = k; i >= t; i--) cout << i << " ";
cout << "\n";
return;
}
int32_t main() {
ios::sync_with_stdio(0), cin.tie(0);
int t;
for (cin >> t; t; t--)
solve();
return 0;
}
D. Program
首先计算出剩下部分的值,然后找到最大最小值就可以知道数字的个数。现在我们考虑如何快速的求出最大最小值,首先删除部分前面的值不会变化。删除部分之后的最值,可以是先求出原始的最值,然后再减去删除部分的变化值即可。这样把两部分的最值合并即为全局的最值。
求区间最最值可以用 ST表解决。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, logN = 21;
int log_2[N];
const int inf = 1e18;
void solve() {
int n, m;
string s;
cin >> n >> m >> s;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
if (s[i - 1] == '-') a[i] = a[i - 1] - 1;
else a[i] = a[i - 1] + 1;
}
vector<array<int, logN>> f(n + 1), g(n + 1);
for (int i = 1; i <= n; i++)
f[i][0] = g[i][0] = a[i];
for (int j = 1; j < logN; j++)
for (int i = 0; i + (1 << j) - 1 <= n; i++) {
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
g[i][j] = min(g[i][j - 1], g[i + (1 << j - 1)][j - 1]);
}
auto getMAX = [f](int l, int r) {
if (l > r) return -inf;
int s = log_2[r - l + 1];
return max(f[l][s], f[r - (1 << s) + 1][s]);
};
auto getMIN = [g](int l, int r) {
if (l > r) return inf;
int s = log_2[r - l + 1];
return min(g[l][s], g[r - (1 << s) + 1][s]);
};
for (int l, r, x, y, d; m; m--) {
cin >> l >> r;
d = a[r] - a[l - 1];
x = max(getMAX(0, l - 1), getMAX(r + 1, n) - d);
y = min(getMIN(0, l - 1), getMIN(r + 1, n) - d);
cout << x - y + 1 << "\n";
}
return;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
log_2[0] = -1;
for (int i = 1; i < N; i++) log_2[i] = log_2[i >> 1] + 1;
int t;
cin >> t;
for (; t; t--) solve();
return 0;
}
但实际上我们只需要用到前缀后缀最值就可以解决这个问题
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 1e18;
void solve() {
int n, m;
string s;
cin >> n >> m >> s;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
if (s[i - 1] == '-') a[i] = a[i - 1] - 1;
else a[i] = a[i - 1] + 1;
}
vector<int> preMAX(n + 1), preMIN(n + 1), sufMAX(n + 2), sufMIN(n + 2);
preMAX[0] = preMIN[0] = a[0];
sufMAX[n + 1] = -inf, sufMIN[n + 1] = inf;
for (int i = 1; i <= n; i++)
preMAX[i] = max(preMAX[i - 1], a[i]), preMIN[i] = min(preMIN[i - 1], a[i]);
for (int i = n; i >= 0; i--)
sufMAX[i] = max(sufMAX[i + 1], a[i]), sufMIN[i] = min(sufMIN[i + 1], a[i]);
for (int l, r, x, y, d; m; m--) {
cin >> l >> r;
d = a[r] - a[l - 1];
x = max(preMAX[l - 1], sufMAX[r + 1] - d);
y = min(preMIN[l - 1], sufMIN[r + 1] - d);
cout << x - y + 1 << "\n";
}
return;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int t;
for (cin >> t; t; t--) solve();
return 0;
}