The 2023 ICPC Asia Jinan Regional Contest (The 2nd Universal Cup. Stage 17: Jinan)

背景

我是菜逼

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值