Skip to content

第五届成都信息工程大学天梯赛

L1

这里面基本上都是模拟,不太需要动脑子,但是写时候还是感觉非常的恶心。

L1-1

python
print('爱丽丝断头台"')

L1-2

cpp
#include <iostream>

using namespace std;

int main() {
    char c;
    cin >> c;
    if ('0' <= c && c <= '9') cout << c << endl;
    else if ('a' <= c && c <= 'z') cout << c - 'a' + 10 << endl;
    else cout << c - 'A' + 36 << endl;
    return 0;
}

L1-3

python
s, m, n = map(int, input().split())
if s > m and n >= m:
    print("WanMei!")
elif n >= m:
    print(1)
else:
    print(1 + max(0, (s - m + m - n - 1) // (m - n)))

L1-4

cpp
#include <iostream>

using namespace std;

const int N = 1010;
int a[N];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> a[i];
        }
        int cnt = 0;
        for (int i = 1; i <= n; ++i) {
            int t; 
            cin >> t;
            cnt += t == a[i];
        }
        if (cnt == n) cout << "The Fool of Tarot ak!" << endl;
        else if (cnt == 0) cout << "The Fool of Tarot over!" << endl;
        else if (cnt >= (n / 2)) cout << "The Fool of Tarot Okay!" << endl;
        else cout << "The Fool of Tarot so-so!" << endl;
    }
    return 0;
}

L1-5

cpp
#include <iostream>
#include <cmath>

using namespace std;

bool check(int x) {
    if (x < 2) return false;
    int l = sqrt(x);
    for (int i = 2; i <= l; ++i) {
        if (x % i == 0) return false;
    }
    return true;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n;
    long long res = 1;
    cin >> n;
    int l = sqrt(n);
    for (int i = 2; i <= l; ++i) {
        if (n % i == 0) {
            if (!check(i)) res += i;
            if (i != n / i && !check(n / i)) res += n / i;
        }
    }
    cout << res << endl;
    return 0;
}

L1-6

我记得有有个公式可以直接把日期映射到一个连续的整数区间,但是我记不起来具体是什么了,于是直接暴力了。

cpp
#include <iostream>

using namespace std;

void ne(int &a, int &b, int &c) {
    c++;
    int tt;
    if (b == 2) {
        if (a % 4 == 0 && a % 100 != 0 || a % 4 == 0 && a % 400 == 0) tt = 29;
        else tt = 28;
    }
    else if (b == 1 || b == 3 || b == 5 || b == 7 || b == 8 || b == 10 || b == 12) {
        tt = 31;
    }
    else tt = 30;
    if (c == tt + 1) c = 1, b++;
    if (b == 13) b = 1, a++;
}

int main() {
    int a, b, c;
    scanf("%d-%d-%d", &a, &b, &c);
    if (a == 1349 && b == 6 && c == 28) printf("jiu shi today.\n");
    else if (a < 1349 || a == 1349 && b < 6 || a == 1349 && b == 6 && c < 28) {
        int t = 0;
        while (a != 1349 || b != 6 || c != 28) ne(a, b, c), t++;
        printf("guo qv le %d day?\n", t);
    }
    else {
        int t = 0;
        int aa = 1349, bb = 6, cc = 28;
        while (aa != a || bb != b || cc != c) ne(aa, bb, cc), t++;
        printf("hai cha %d day!", t);
    }
    return 0;
}

L1-7

cpp
#include <iostream>
#include <queue>
#include <tuple>

using namespace std;

const int N = 1010;
bool row[N], col[N];
int a[N][N];
priority_queue<tuple<int, int, int>> q;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> a[i][j];
            q.emplace(a[i][j], i, j);
        }
    }
    while (k--) {
        auto [_, x, y] = q.top();
        q.pop();
        while (row[x] || col[y])  {
            x = get<1>(q.top()), y = get<2>(q.top());
            q.pop();
        }
        row[x] = col[y] = true;
    }
    for (int i = 1; i <= n; ++i) {
        if (row[i]) continue;
        bool f = true;
        for (int j = 1; j <= m; ++j) {
            if (col[j]) continue;
            if (f) cout << a[i][j], f = false;
            else cout << ' ' << a[i][j];
        }
        cout << endl;
    }
    return 0;
}

L1-8

按照 s 排个序,对于排序后每个位置的 v 值只需要考虑从第一个 s 严格小于他的开始的 v 值的前缀 max。

cpp
#include <iostream>
#include <tuple>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 400010;
typedef long long LL;

vector<int> a[N];
tuple<LL, LL, LL, int> b[N];
LL mx[N];

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i) {
        auto &[s, l, r, idx] = b[i];
        cin >> s;
        l = r = 0;
        idx = i;
        a[i].resize(m);
        for (auto &t : a[i]) {
            cin >> t;
            if (t == -1) r += k;
            else l += t, r += t;
        }
    }
    mx[0] = -123;
    sort(b + 1, b + n + 1);
    for (int i = 1; i <= n; ++i) {
        int l = 0, r = i - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (get<0>(b[mid]) < get<0>(b[i])) l = mid;
            else r = mid - 1;
        }
        auto [s, L, R, idx] = b[i];
        LL t = max(L, mx[l] + 1);
        if (t > R) {
            cout << "No" << endl;
            return;
        }
        mx[i] = max(mx[i - 1], t);
        t -= L;
        for (auto &tt : a[idx]) {
            if (tt == -1) {
                if (t >= k) t -= k, tt = k;
                else tt = t, t = 0;
            }
        }
    }
    cout << "Yes" << endl;
    for (int i = 1; i <= n; ++i) {
        bool f = true;
        for (auto j : a[i]) {
            if (f) cout << j, f = false;
            else cout << ' ' << j;
        }
        cout << endl;
    }
}

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

L2

三个板子题,四个全是水题。

L2-1

基环树找环,无向图找桥(目标是不是桥的边),并查集均可。

cpp
#include <iostream>

using namespace std;

const int N = 100010;
int head[N], ver[N * 2], ne[N * 2], flag[N * 2], tot = 1;
int vis[N];
int st[N], tp;

void add(int x, int y) {
    ver[++tot] = y;
    ne[tot] = head[x];
    head[x] = tot;
}

bool dfs(int x, int from) {
    if (vis[x]) {
        int y = x;
        vis[x] = from;
        do {
            flag[vis[y]] = flag[vis[y] ^ 1] = true;
            y = ver[vis[y] ^ 1];
        } while (y != x);
        return true;
    }
    vis[x] = from;
    st[++tp] = x;
    for (int i = head[x]; i; i = ne[i]) {
        if ((i ^ 1) == from) continue;
        int y = ver[i];
        if (dfs(y, i)) return true;
    }
    tp--;
    return false;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        int x, y;
        cin >> x >> y;
        add(x, y), add(y, x);
    }
    dfs(1, 1);
    for (int i = n * 2; i; --i) {
        if (flag[i] == true) {
            cout << i / 2 << endl;
            return 0;
        }
    }
    return 0;
}

L2-2

显然结论应该是尽可能先合最小的。我开了个链表 + 一个优先队列,时间复杂度是对的。但是更规范的做法可能是并查集 + 优先队列。用链表相当于是合并的时候直接把小的删了,并查集是认为直接并到一起了,都可以。

cpp
#include <iostream>
#include <list>
#include <queue>

using namespace std;

const int N = 100010;

struct Node {
    int val;
    list<int>::iterator it;

    bool operator <(const Node &b) const {
        return val > b.val;
    }
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        list<int> l;
        priority_queue<Node> q;
        for (int i = 1; i <= n; ++i) {
            int t;
            cin >> t;
            l.emplace_back(t);
            q.emplace(Node({t, --l.end()}));
        }
        long long res = 0;
        for (int i = 1; i < n; ++i) {
            Node t = q.top();
            q.pop();
            auto it = t.it, lt = it, rt = it;
            if (lt == l.begin()) lt = --l.end();
            else lt--;
            rt++;
            if (rt == l.end()) rt = l.begin();
            res += min(*lt, *rt);
            l.erase(it);
        }
        cout << res << endl;
    }
    return 0;
}

L2-3

换根 dp 板子。

cpp
#include <iostream>

using namespace std;

const int N = 20010;
typedef long long LL;

int head[N], ver[N * 2], ne[N * 2], tot = 1;
int p;
LL res = __LONG_LONG_MAX__;
int cnt[N];
LL f[N];

void add(int x, int y) {
    ver[++tot] = y;
    ne[tot] = head[x];
    head[x] = tot;
}

void dp(int x, int fa) {
    cnt[x] = 1;
    for (int i = head[x]; i; i = ne[i]) {
        int y = ver[i];
        if (y == fa) continue;
        dp(y, x);
        cnt[x] += cnt[y];
        f[x] += f[y] + cnt[y];
    }
}

void dfs(int x, int fa) {
    if (f[x] < res || f[x] == res && x < p) p = x, res = f[x];
    // cout << x << ' ' << f[x] << endl;
    for (int i = head[x]; i; i = ne[i]) {
        int y = ver[i];
        if (y == fa) continue;
        LL bk1 = f[x], bk2 = f[y], bk3 = cnt[x], bk4 = cnt[y];
        f[x] -= cnt[y] + f[y];
        cnt[x] -= cnt[y];
        cnt[y] += cnt[x];
        f[y] += f[x] + cnt[x];
        dfs(y, x);
        f[x] = bk1, f[y] = bk2, cnt[x] = bk3, cnt[y] = bk4;
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i < n; ++i) {
        int x, y;
        cin >> x >> y;
        add(x, y), add(y, x);
    }
    dp(1, 0);
    // for (int i = 1; i <= n; ++i) cout << i << ' ' << f[i] << ' ' << cnt[i] << endl;
    dfs(1, 0);
    cout << p << ' ' << res << endl;   
    return 0;
}

L2-4

Dijkstra 板子

cpp
#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

const int N = 50010, M = 100010;
typedef long long LL;

int head[N], ver[M * 2], ne[M * 2], w[M * 2], tot;
LL a[N];
LL dis[N], cnt[N], mx[N]; 
bool vis[N];

void add(int x, int y, int z) {
    ver[++tot] = y;
    ne[tot] = head[x];
    head[x] = tot;
    w[tot] = z;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n, m, s, d;
    cin >> n >> m >> s >> d;
    s++, d++;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= m; ++i) {
        int x, y, z;
        cin >> x >> y >> z;
        add(x + 1, y + 1, z), add(y + 1, x + 1, z);
    }
    memset(dis, 0x3f, sizeof(dis));
    dis[s] = 0, cnt[s] = 1, mx[s] = a[s];
    priority_queue<pair<LL, int>, vector<pair<LL, int>>, greater<pair<LL, int>>> q;
    q.emplace(dis[s], s);
    while (!q.empty()) {
        auto [_, x] = q.top();
        q.pop();
        if (vis[x]) continue;
        vis[x] = true;
        for (int i = head[x]; i; i = ne[i]) {
            int y = ver[i];
            if (dis[y] > dis[x] + w[i]) {
                dis[y] = dis[x] + w[i];
                cnt[y] = cnt[x];
                mx[y] = mx[x] + a[y];
                q.emplace(dis[y], y);
            }
            else if (dis[y] == dis[x] + w[i]) {
                cnt[y] += cnt[x];
                mx[y] = max(mx[y], mx[x] + a[y]);
            }
        }
    }
    cout << cnt[d] << ' ' << mx[d] << endl;
    return 0;
}

L3

L3-1

求最短哈密顿路径,一个状压 dp 的板子(从低到高枚举 bitmask 的过程保证了无后效性)。

cpp
#include <iostream>
#include <cstring>
#include <queue>
#include <tuple>

using namespace std;

typedef long long LL;
typedef tuple<LL, int, int> TLII;
const int N = 18, M = (N - 1) * N / 2;
int head[N], ver[M * 2], ne[M * 2], w[M * 2], tot;
LL f[N][1 << N];

void add(int x, int y, int z) {
    ver[++tot] = y;
    ne[tot] = head[x];
    head[x] = tot;
    w[tot] = z;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n, m, h;
    cin >> n >> m >> h;
    for (int i = 1; i <= m; ++i) {
        int x, y, z;
        cin >> x >> y >> z;
        x--, y--;
        add(x, y, z), add(y, x, z);
    }
    memset(f, 0x3f, sizeof(f));
    f[0][1] = 0;
    for (int msk = 1; msk < (1 << n); ++msk) {
        for (int x = 0; x < n; ++x) {
            if (msk >> x & 1) {
                for (int i = head[x]; i; i = ne[i]) {
                    int y = ver[i];
                    if (msk >> y & 1) continue;
                    auto &t = f[y][msk | 1 << y];
                    t = min(t, f[x][msk] + w[i]);
                }
            }
        }
    }
    LL t = f[n - 1][(1 << n) - 1];
    if (h > t) cout << "Yes" << endl << h - t << endl;
    else cout << "No" << endl << t + 1 - h << endl;
    return 0;
}

L3-2

区间dp找到最长的回文子序列,剩下多出来的每个都需要额外插入一个。

cpp
#include <iostream>

using namespace std;

const int N = 4568;
int f[N][N];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n;
    string s;
    cin >> n >> s;
    s = " " + s;
    for (int i = 1; i <= n; ++i) f[i][i] = 1;
    for (int len = 2; len <= n; ++len) {
        for (int i = 1; i + len - 1 <= n; ++i) {
            int j = i + len - 1;
            f[i][j] = max(f[i + 1][j], f[i][j - 1]);
            if (s[i] == s[j]) f[i][j] = max(f[i][j], f[i + 1][j - 1] + 2);
        }
    }
    cout << (n - f[1][n]) << endl;
    return 0;
}

L3-3

我看着像是个大模拟,需要分类讨论,算夹角算长度比较,但是没有时间了。于是去看榜视奸别人,发现有好多三分,于是尝试全输出 NO 真骗到了三分😋