背景
我是菜逼
A.Many Many Heads
思路
怎样才是合法括号序列?首先是"("的数量等于")"的数量,其次是从左往右遍历的过程中"("的数量始终不少于")"的数量。(但是这题没有任何关系)
用来维护合法括号序列的有用数据结构是栈
首先先把这个序列给调成合法的,其次就是判断是否唯一
1.遇到")("或者"]["
2.遇到")[X]("或者"](X)["
X是一个合法括号序列
代码
#include <bits/stdc++.h>
#define int long long
#define vv vector
using namespace std;
using PII = pair<int, int>;
/*
sad
*/
void solve() {
string s; cin >> s;
int n = s.size();
vv<char> st;
for (char &c : s) {
if (st.empty()) {
if (c == ')') c = '(';
else if (c == ']') c = '[';
st.push_back(c);
}else {
if (st.back() == '(') {
if (c == '(') c = ')', st.pop_back();
else if (c == ')') st.pop_back();
else c = '[', st.push_back(c);
}else if (st.back() == '[') {
if (c == '[') c = ']', st.pop_back();
else if (c == ']') st.pop_back();
else c = '(', st.push_back(c);
}else {
if (c == ']') c = '[';
if (c == ')') c = '(';
st.push_back(c);
}
}
}
bool ok = true;
vv<int> pos(n + 2), st1, st2;
s = ' ' + s + ' ';
for (int i = 1; i <= n; i ++ ) {
if (s[i] == '(') st1.push_back(i);
else if (s[i] == '[') st2.push_back(i);
else {
if (s[i] == ']') pos[st2.back()] = i, st2.pop_back();
else pos[st1.back()] = i, st1.pop_back();
}
}
for (int i = 1; i < n; i ++ ) {
if (s[i] == ')' && s[i + 1] == '(') ok = false;
if (s[i] == ']' && s[i + 1] == '[') ok = false;
if (s[i] == ')' && pos[i + 1] && s[pos[i + 1] + 1] == '(') ok = false;
if (s[i] == ']' && pos[i + 1] && s[pos[i + 1] + 1] == '[') ok = false;
}
if (ok) puts("Yes");
else puts("No");
return;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt = 1; cin >> tt;
while (tt -- ) solve();
return 0;
}
D.Largest Digit
思路
如果l和r之间相差10,那么就一定可以由两个数组出来一个9
否则直接暴力就行了
代码
#include <bits/stdc++.h>
#define int long long
#define vv vector
using namespace std;
using PII = pair<int, int>;
/*
sad
*/
void solve() {
int l, r, l1, r1; cin >> l >> r >> l1 >> r1;
int ans = -1;
if (r - l >= 10 || r1 - l1 >= 10) ans = 9;
else {
for (int i = l; i <= r; i ++ ) {
for (int j = l1; j <= r1; j ++ ) {
int cur = i + j;
while (cur) {
ans = max(ans, cur % 10);
cur /= 10;
}
}
}
}
cout << ans << endl;
return;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
srand(time(0));
int tt = 10000; cin >> tt;
while (tt -- ) solve();
return 0;
}
G.Gifts from Knowledge
思路
首先,如果第i列和第m-i+1列的“1”的数量超过了2,那么是不可能的
记原字符串为S,翻转后的字符串为T
现在遇到了两个字符串Si,Ti,Sj,Tj
如果两个字符串在第k的位置上都有“1”,那么Si一定是跟Tj在一块的,Sj一定是跟Ti在一块的
如果两个字符串在第k和m-k+1的位置上都有“1”,那么Si一定是跟Sj在一块的,Ti一定是跟Tj在一块的
我们用并查集维护这一个“在一块”
fa[i]放Si,fa[i+n]放Ti
最后只需要判断Si和Ti是否在同一个连通块即可
对于答案
如果符合题意,我们一定是得到偶数个连通块的,记为n
那么可以选S也可以选T,就会有2的n/2次贡献
代码
#include <bits/stdc++.h>
#define int long long
#define vv vector
using namespace std;
using PII = pair<int, int>;
/*
Have a nice day!
*/
const int mod = (int)1e9 + 7;
int qpow(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
void solve() {
int n, m; cin >> n >> m;
vv<vv<int>> col(2 * m + 1);
for (int i = 1; i <= n; i ++ ) {
string s; cin >> s;
s = ' ' + s;
for (int j = 1; j <= m; j ++ ) if (s[j] - '0') col[j].push_back(i);
}
for (int i = 1; i <= (m + 1) / 2; i ++ ) {
if (col[i].size() + col[m - i + 1].size() > 2) return cout << 0 << endl, void();
}
vv<int> fa(2 * n + 1);
function<int(int)> find = [&](int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
};
iota(fa.begin(), fa.end(), 0);
for (int i = 1; i <= m / 2; i ++ ) {
if (col[i].size() == 2) {
int f1 = find(col[i][0]), f4 = find(col[i][1] + n);
fa[f4] = f1;
int f2 = find(col[i][1]), f3 = find(col[i][0] + n);
fa[f3] = f2;
}else if (col[m - i + 1].size() == 2) {
int f1 = find(col[m - i + 1][0]), f4 = find(col[m - i + 1][1] + n);
fa[f4] = f1;
int f2 = find(col[m - i + 1][1]), f3 = find(col[m - i + 1][0] + n);
fa[f3] = f2;
}else if (col[i].size() && col[m - i + 1].size()) {
int f1 = find(col[i][0]), f2 = find(col[m - i + 1][0]);
fa[f2] = f1;
int f3 = find(col[i][0] + n), f4 = find(col[m - i + 1][0] + n);
fa[f3] = f4;
}
}
for (int i = 1; i <= n; i ++ ) if (find(i) == find(i + n)) return cout << 0 << endl, void();
vv<int> vis(2 * n + 1);
int ans = 0;
for (int i = 1; i <= 2 * n; i ++ ) {
if (!vis[find(i)]) {
vis[find(i)] = 1;
ans += 1;
}
}
ans /= 2;
cout << qpow(2, ans) << endl;
return;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt = 1; cin >> tt;
while (tt -- ) solve();
return 0;
}
I.Strange Sorting
思路
先进行题目意思的转化,每次可以选择一个逆序对,然后使得这个区间内的数变成升序。
题目中的n/2是哪里来的?
2 1 4 3 6 5
相邻的两个元素分别要sort一下,所以是n/2次
那么我们从左往右进行遍历,当a[i] != i的时候,我们就必须选择一个逆序对进行sort,这时候我们选择哪一个呢?
1 2 5 4 3
当我们到5的时候,我们天然的会去选3,直接sort完毕
1 2 5 3 4
我们会去选4,也是直接sort完毕
明显地是
1 2 5 4 3
我们sort 5,4的话,会是
1 2 4 5 3
并不能使得3归位
1 2 5 3 4
如果sort 5,3的话
1 2 3 5 4
会被卡n/2的需求
那么我们是不是只要找离它最远的一个逆序对就可以了?
可以证明的是,这样一定可以sort一遍使得两个数归位
代码
#include <bits/stdc++.h>
#define int long long
#define vv vector
using namespace std;
using PII = pair<int, int>;
/*
Have a nice day!
*/
void solve() {
int n; cin >> n;
vv<int> a(n + 1);
for (int i = 1; i <= n; i ++ ) cin >> a[i];
vv<PII> ans;
for (int i = 1; i <= n; i ++ ) {
if (a[i] == i) continue;
int id = -1;
for (int j = i + 1; j <= n; j ++ ) if (a[j] < a[i]) id = j;
ans.push_back({i, id});
sort(a.begin() + i, a.begin() + id + 1);
}
cout << ans.size() << endl;
for (auto [l, r] : ans) cout << l << ' ' << r << endl;
return;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt = 1; cin >> tt;
while (tt -- ) solve();
return 0;
}
K.Rainbow Subarray
思路
判断是否为连续子数组的有效方式是,对于每一个i,都让ai减去i(cf上做过)
那么问题就转化成了,在k次操作下,所包含元素都相同的区间长度是多少
然后再进行思考,使得区间里的元素都变成相同的最小花费是多少?
这个问题就是路程最短问题,是变成这些元素中的中位数。
中位数如果用二分去做的话,是去枚举长度的,最优也是两个log,是错误的
因为连续,我们考虑滑动窗口,中位数的话用set维护即可
代码
#include <bits/stdc++.h>
#define int long long
#define vv vector
using namespace std;
using PII = pair<int, int>;
/*
sad
*/
void solve() {
int n, k; cin >> n >> k;
vv<int> a(n + 1);
for (int i = 1; i <= n; i ++ ) {
cin >> a[i], a[i] -= i;
}
multiset<int> SA, SB;
int suma = 0, sumb = 0;
SA.insert(a[1]), suma += a[1];
int ans = 1;
function<void()> clear = [&]() {
while (SA.size() > SB.size() + 1) {
int x = *SA.rbegin();
SB.insert(x); sumb += x;
SA.erase(SA.find(x)); suma -= x;
}
while (SB.size() > SA.size()) {
int x = *SB.begin();
SA.insert(x); suma += x;
SB.erase(SB.find(x)); sumb -= x;
}
while (SB.size() && (*SA.rbegin()) > (*SB.begin())) {
int x = (*SA.rbegin()), y = (*SB.begin());
suma -= x, sumb -= y;
SA.erase(SA.find(x)), SB.erase(SB.find(y));
SA.insert(y), SB.insert(x);
suma += y, sumb += x;
}
};
function<int()> calc = [&]() {
int res = (sumb - SB.size() * (*SA.rbegin()));
res += (SA.size() * (*SA.rbegin()) - suma);
return res;
};
for (int i = 2, j = 1; i <= n; i ++ ) {
SA.insert(a[i]), suma += a[i];
clear();
while (j <= n && calc() > k) {
if (SB.count(a[j])) SB.erase(SB.find(a[j])), sumb -= a[j];
else SA.erase(SA.find(a[j])), suma -= a[j];
j += 1;
clear();
}
clear();
ans = max(ans, (int)SA.size() + (int)SB.size());
}
cout << ans << endl;
return;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt = 1; cin >> tt;
while (tt -- ) solve();
return 0;
}