SMU Spring 2023 Trial Contest Round 1

发布时间 2023-03-22 21:18:31作者: PHarr

A. Prepend and Append

如果两段字符不同就可以删掉,如果不能删了就是最初的字符串

#include <bits/stdc++.h>

using namespace std;

void solve() {
    int n;
    string s;
    cin >> n >> s;
    int l = 0, r = n - 1;
    while (l <= r && s[l] != s[r]) l++, r--;
    cout << (r - l + 1) << "\n";
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

B. Distinct Split

维护前缀集合和后缀集合即可

#include <bits/stdc++.h>

using namespace std;

void solve() {
    int n, res = 0, va = 0, vb = 0;
    string s;
    cin >> n >> s;
    map<char, int> b, a;
    a[s[0]]++;
    for (int i = 1; i < n; i++) b[s[i]]++;
    for (auto [k, v]: a)
        if (v > 0) va++;
    for (auto [k, v]: b)
        if (v > 0) vb++;
    res = max( res , va + vb );
    for( int i = 1 ; i < n-1 ; i ++ ){
        a[ s[i] ] ++;
        if( a[ s[i] ] == 1 ) va ++;
        b[ s[i] ] --;
        if( b[ s[i] ] == 0 ) vb --;
        res = max( res , va + vb );
    }
    cout << res << "\n";
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

C. Negatives and Positives

如果先操作i,然后操作i+1,i+2,i+3,...,i+k,实际上等价于任意选两个数取反,所以要做的就是把正数和负数分开,如果负数是偶数就全部转换成正数,如果是奇数,就先尽可能的把小的负数转成正数,然后考虑吧最大的负数和最小的正数同时取反是否更优即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

void solve() {
    int n = read(), res = 0;
    vector<int> a, b;
    for (int x; n; n--) {
        x = read();
        if (x >= 0) a.push_back(x);
        else b.push_back(x);
    }
    sort(a.begin(), a.end()), sort(b.begin(), b.end());
    for (auto i: a) res += i;
    if (b.size() & 1) {
        for (int i = 0; i < b.size() - 1; i++)
            res -= b[i];
        if (!a.empty()) res += max(b.back(), -2 * a.front() - b.back());
        else res += b.back();
    } else for (auto i: b) res -= i;
    cout << res << "\n";
    return;
}

int32_t main() {
    for (int t = read(); t; t--) solve();
    return 0;
}

D. Range Update Point Query

实际上对于一个数,操作不了多少次就会变成操作无效的数。所以可以维护懒标记的方式记录被操作次数,当实际询问的时候才去操作。

然后考虑懒标记的维护实际上就是区间修改、单点修改、单点查询,用树状数组维护一下就好了

#include <bits/stdc++.h>

using namespace std;

#define lowbit(x) ( x & -x )

const int N = 2e5 + 5;
int n , q;
int a[N], b[N];

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int getVal(int x) {
    int ans = 0;
    for (int i = x; i; i -= lowbit(i)) ans += b[i];
    return ans;
}

void add(int x, int val) {
    for (int i = x; i <= n; i += lowbit(i))
        b[i] += val;
    return;
}

void update(int l, int r, int v) {
    add(l, v), add(r + 1, -v);
    return;
}

int modify(int x) {
    int ans = 0;
    while (x) ans += x % 10, x /= 10;
    return ans;
}

void solve() {
    n = read(), q = read();
    fill( b , b+1+n , 0 );
    for (int i = 1; i <= n; i++) a[i] = read();
    for (int op, l, r, x, t; q; q--) {
        op = read();
        if (op == 1) l = read(), r = read(), update(l, r, 1);
        else {
            l = read(), x = getVal(l);
            update( l , l , -x );
            while (x--) {
                t = modify(a[l]);
                if (t == a[l]) break;
                a[l] = t;
            }
            printf("%d\n" , a[l] );
        }
    }
    return;
}

int32_t main() {
    for (int t = read(); t; t--) solve();
    return 0;
}

E. Dima and Guards

这个其实就是求最小值就好了,注意一定要花 n 元而不是最多花 n 元

#include <bits/stdc++.h>

using namespace std;

int32_t main() {
    int n , ans = -1, x , y ;
    cin >> n;
    for( int a , b , c , d , i = 1 ; i <= 4 ; i ++ ){
        cin >> a >> b >> c >> d;
        a = min( a , b ) , c = min( c , d );
        if( a + c <= n ) {
            ans = i , x = a , y = n - x;
            break;
        }
    }
    if( ans == -1 ) cout << "-1\n";
    else cout << ans << " " << x << " " << y << "\n";
    return 0;
}

F. Dima and To-do List

根据\(i\mod k\)分一下类,然后求和取最小值即可

#include <bits/stdc++.h>

using namespace std;

#define int long long

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    int n = read() , k = read() , ans = 1, ansV = INT_MAX;

    vector<int> f(k , 0 );
    for( int i = 1 , x ; i <= n ; i ++ ) x = read() , f[i%k] += x;
    for( int i = 1 ; i <= k ; i ++ )
        if( f[i%k] < ansV ) ans = i , ansV = f[i];
    cout << ans << "\n";
    return 0;
}

G. Dima and Salad

\[\frac{\sum a_i}{\sum b_i} = k\\ \sum a_i - k\times \sum b_i = 0\\ \sum(a_i-kb_i)=0 \]

这样可以转化为\(v_i=a_i-kb_i,w_i=a_i\)的零一背包问题,只是背包的容量是\(0\),且\(v_i\)有正有负

\(v_i\)按照正负分类后再分别求背包,f[i]表示\(\sum v = i\)\(\sum w\)的最大值,g[i]表示\(\sum v=-i\)\(\sum w\)的最大值,所以答案就是max( f[i] + g[i] )

#include <bits/stdc++.h>

using namespace std;

#define int long long
int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    int n = read(), k = read(), m = 1e4;
    vector<int> v(n + 1), w(n + 1);
    for (int i = 1; i <= n; i++) w[i] = read();
    for (int i = 1, x; i <= n; i++)
        x = read(), v[i] = w[i] - x * k;

    vector<int> f(m + 1, INT_MIN) , g(m+1 , INT_MIN);
    f[0] = g[0] = 0;
    for (int i = 1; i <= n; i++) {
        if (v[i] >= 0) {
            for (int j = m; j >= v[i]; j--)
                f[j] = max(f[j], f[j - v[i]] + w[i]);
        } else {
            v[i] *= -1;
            for (int j = m; j >= v[i]; j--)
                g[j] = max(g[j], g[j - v[i]] + w[i]);
        }
    }
    int res = 0;
    for( int i = 0 ; i <= m ; i ++ )
        res = max( res , f[i] + g[i] );
    if( res == 0 ) cout << "-1\n";
    else cout << res << "\n";
    return 0;
}

H. Dima and Trap Graph

(u,l,r)表示到达u时所能携带数的区间。然后做一个搜索加剪枝即可。

剪枝如下:

  1. 最优性剪枝,如果当前区间小于已知解,停止搜索。
  2. 记忆化,用set维护每个点被访问时的区间,如果重复出现,停止搜索。

很怪 dfs会 tle,但bfs跑的飞快。

#include<bits/stdc++.h>

using namespace std;

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    int n = read() , res = 0;
    vector<vector<tuple<int, int, int>>> e(n + 1);
    vector<set<pair<int, int>>> vis(n + 1);
    for (int u, v, l, r, m = read(); m; m--)
        u = read(), v = read(), l = read(), r = read(), e[u].emplace_back(v, l, r), e[v].emplace_back(u, l, r);
    vis[1].emplace(1, 1e6);
    queue<tuple<int, int, int> > q;
    q.emplace(1, 1, 1e6);
    while (!q.empty()) {
        auto [u, l, r] = q.front();
        q.pop();
        if (u == n) {
            res = max(res, r - l + 1);
            continue;
        }
        int lt, rt;
        for (auto [v, lk, rk]: e[u]) {
            lt = max(l, lk), rt = min(r, rk);
            if (lt > rt || rt - lt + 1 <= res) continue;
            if (vis[v].emplace(lt, rt).second == false) continue;
            q.emplace(v, lt, rt);
        }
    }

    if (res == 0) cout << "Nice work, Dima!";
    else cout << res;
    return 0;
}