第 45 屆國際大學生程序設計競賽(ICPC)亞洲區域賽(昆明)


第 45 屆國際大學生程序設計競賽(ICPC)亞洲區域賽(昆明)

BEF 沒補, 出題人定義的是hard, 是給金牌S+隊伍寫的, 菜雞根本寫不到, 寫到了考場也出不了, 就放棄了

A - AC

和數據備份一樣,

反悔貪心模型, 用堆來維護

\((i, i + 1)\) 變成 \(ac\), 下次后悔這部操作, 而改成 \((i - 1, i) (i + 1, i + 2)\)

至於怎么保存修改的位置? 對每個操作的位置維護一個區間, 表示這步是將 \([l, r]\) 改成 \(ac\)

當這步操作從對選出來的時候, 直接暴力打個標記表示 \([l, r]\) 要改

注意到上次選出來這個操作是 \([l', r']\)\(l < l', r' < r\) 我們需要打標記的位置是基於上一次的, 最終打標記的復雜度還是 \(O(n)\)

輸出字符串的時候, 對於每個打標記的去點左端點開始 變成 'a' 知道這個區間結束, 這個區間的長度一定是偶數

#include <bits/stdc++.h>
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
using PII = pair<int, int>;

const int N = 5e5 + 5;

int n, k, m, pre[N], nxt[N], l[N], r[N], c[N];
char s[N], t[] = "ac";
bool v[N], g[N];

void del(int x) { nxt[pre[x]] = nxt[x]; pre[nxt[x]] = pre[x]; }

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> k >> s + 1; priority_queue<PII> q;
    rep(i, 1, n - 1) {
        c[i] = (s[i] != 'a') + (s[i + 1] != 'c'); q.push({ -c[i], i });
        pre[i] = i - 1, nxt[i] = i + 1; l[i] = i, r[i] = i + 1;
    } pre[n] = n - 1; r[0] = 1;
    while (m < n / 2) {
        int x = q.top().se; q.pop();
        if (v[x]) continue; k -= c[x];
        if (k < 0) break; ++m;
        rep(i, l[x], r[x]) if (!g[i]) g[i] = 1; else break;
        per(i, r[x], l[x]) if (!g[i]) g[i] = 1; else break;
        v[pre[x]] = v[nxt[x]] = 1;
        if (pre[x] && nxt[x] != n) {
            l[x] = l[pre[x]], r[x] = r[nxt[x]];
            c[x] = c[pre[x]] + c[nxt[x]] - c[x];
            del(pre[x]), del(nxt[x]); q.push({ -c[x], x });
        }
        else if (pre[x] == 0 && nxt[x] != n) del(x), del(nxt[x]);
        else if (pre[x] && nxt[x] == n) del(pre[x]), del(x);
    }
    cout << m << '\n';
    rep(i, 1, n) if (g[i]) s[i] = 'a', s[++i] = 'c'; cout << s + 1;
    return 0;
}

C - Cities

很模板的區間dp, 本來是要\(O(n^3)\) 的, 但由於每個領主最多只有15坐城,

尋找區間分割點的時候可以直接枚舉和 區間右端點屬於相同領主的點即可, 則可優化到 \(O(15 * n^2)\)

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;

template<class T1, class T2> bool umin(T1& a, T2 b) { return a > b ? (a = b, true) : false; }

const int N = 5e3 + 5;

int n, m, _;
int a[N], f[N][N], pre[N], ls[N];

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    for (cin >> _; _; --_) {
        cin >> n; rep (i, 1, n) ls[i] = 0;
        rep (i, 1, n) {
            cin >> a[i];
            if (a[i] == a[i - 1]) --i, --n;
            else pre[i] = ls[a[i]], ls[a[i]] = i;
        }
        per (i, n, 1) rep (j, i + 1, n) {
            f[i][j] = f[i][j - 1] + 1;
            if (a[i] == a[j]) umin(f[i][j], f[i + 1][j - 1] + 1);
            for (int k = pre[j]; k > i; k = pre[k]) umin(f[i][j], f[i][k - 1] + f[k][j] + (a[i] != a[j]));
        }
        cout << f[1][n] << '\n';
    }
    return 0;
}

D - Competition Against a Robot

結論題, 靠猜, 想要證明去知乎吧, 太麻煩了, 等你證出來比賽結束了, 數論大佬當我沒說

一共有\(k^n\)個序列, 對於序列你可以讓這個序列, 變成其他\(n\)個中的一個, 最好的划分是,

讓當前可變成的序列分別代表一\(p\)的值, 正好對應\(p\) 屬於 [0, n), 大膽的猜

\(n | k^n\) 有解, 每個序列代表一個數字, 代表數字 x 的有序列集合大小相等

至於怎么求是否整除, gcd就行

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a)i<=(b);++i)
using namespace std;
using ll = long long;

ll n, m, _, k;

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    for (cin >> _; _; --_) {
        cin >> n >> k;
        for (k = __gcd(k, n); n - 1 && k - 1; k = __gcd(n /= k, k));
        cout << (n - 1 ? "ROBOT\n" : "HUMAN\n");
    }
    return 0;
}

G - Gift

又是dp模型

先對朋友按生日升序排序

\(f(i, j, k)\) 表示第\(i\)個朋友花了\(j\)天做蛋糕送了\(k\)個禮物的最大滿意度

轉移方程就很好寫了,(中間的合法在代碼里看吧)

啥都不干\(f(i, j, k) = f(i - 1, j, k)\)

\(i\)做蛋糕\(f(i, j, k) = f(i - 1, j - c_i, k) + v_i\)

\(i\)送禮物\(f(i, j, k) = f(i - 1, j - c_i, k - 1) - mx_{k - 1} + mx_k\)

其中\(mx_i\) 表示只送\(i\)個禮物能獲得的最大滿意度

千萬別忘了2021, 沒有2.29

#include <bits/stdc++.h>
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
using PII = pair<int, int>;
using ll = long long;

const int N = 505;

struct node { int y, m, d, c, v; } a[N];

int n, m, _, w;
int day[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
ll f[370][20], d[20];
PII b[20];

int main() {
    for (scanf("%d", &_); _; --_) {
        scanf("%d%d%d", &n, &m, &w); ll mx = 0;
        memset(f, 0xcf, sizeof f); f[0][0] = 0; memset(d, 0xcf, sizeof d);
        rep(i, 1, n) {
            scanf("%d-%d-%d %d %d", &a[i].y, &a[i].m, &a[i].d, &a[i].c, &a[i].v); a[i].y = a[i].d;
            if (a[i].m == 2 && a[i].d == 29) { --i, --n; continue; }
            rep(j, 1, a[i].m - 1) a[i].y += day[j];
        }
        rep(i, 0, m - 1) scanf("%d%d", &b[i].fi, &b[i].se);
        sort(a + 1, a + 1 + n, [](node& a, node& b) { return a.y < b.y; });
        rep(i, 0, (1 << m) - 1) {
            ll c = 0, v = 0, g = 0;
            rep(j, 0, m - 1) if (i >> j & 1) {
                c += b[j].fi; ++g; v += b[j].se;
                if (c > w) break;
            }
            if (c <= w) d[g] = max(d[g], v);
        }
        while (d[m] < 0) --m;
        rep(i, 1, n) per(j, a[i].y, 0) per(k, m, 0) {
            if (j >= a[i].c) f[j][k] = max(f[j][k], f[j - a[i].c][k] + a[i].v);
            if (k) f[j][k] = max(f[j][k], f[j][k - 1] - d[k - 1] + d[k]);
            mx = max(mx, f[j][k]);
        }
        cout << mx << '\n';
    }
    return 0;
}

H - Hard Calculation

溫暖人心

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n; cin >> n; cout << 2020 + n;
    return 0;
}

I - Mr. Main and Windmills

求每個風車和其他風車的連線與線段\(ST\)的交點距離\(S\)的距離排序就行

#include <bits/stdc++.h>
using namespace std;

struct Point {
    double x, y; Point(double X = 0, double Y = 0) { x = X, y = Y; }
    inline void in() { cin >> x >> y; }
    inline void out() { cout << setiosflags(ios::fixed) << setprecision(10) << x << ' ' << y << '\n'; }   
};

inline double Cro(Point a, Point b) { return a.x * b.y - a.y * b.x; }
inline Point operator-(Point a, Point b) { return Point(a.x - b.x, a.y - b.y); }
inline Point operator+(Point a, Point b) { return Point(a.x + b.x, a.y + b.y); } 
inline Point operator*(Point a, double b) { return Point(a.x * b, a.y * b); }

inline Point cross_LL(Point a, Point b, Point c, Point d, double& len) {
    Point x = b - a, y = d - c, z = a - c; len = Cro(y, z) / Cro(x, y);
    return a + x * len;
}

int n, m;
Point s, e, a[1005];
vector<pair<double, Point>> v[1005];

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> m; s.in(); e.in();
    for (int i = 1; i <= n; ++i) {
        a[i].in();
        for (int j = 1; j < i; ++j) {
            double len; Point x = cross_LL(s, e, a[i], a[j], len);
            if (len > 1 || len < 0) continue;
            v[i].emplace_back(len, x); v[j].emplace_back(len, x);
        }
    }
    for (int i = 1; i <= n; ++i) sort(v[i].begin(), v[i].end(), [](pair<double, Point>& a, pair<double, Point>& b) { return a.first < b.first; });
    for (int i = 1; i <= m; ++i) {
        int k, h; cin >> k >> h;
        if (v[k].size() < h) { cout << "-1\n"; continue; }
        v[k][h - 1].second.out();
    }
    return 0;
}

J - Parallel Sort

主要處理置換環的問題, 而我們可以很輕松的用一次, 把環拆成兩個兩個的環,

遞歸處理即可

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;

int n, a[N], b[N];

void dfs(int x, int y, vector<int>& c) {
    if (a[x] == y) return; int t = b[y];
    c.emplace_back(x), c.emplace_back(b[y]);
    a[t] = a[x]; b[a[x]] = t; a[x] = y; b[y] = x;
    if (t != a[t]) dfs(a[t], t, c);
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i], b[a[i]] = i;
    vector<vector<int>> ans(1);
    for (int i = 1; i <= n; ++i) if (a[i] != i) dfs(a[i], i, ans.back());
    if (!ans.back().empty()) ans.emplace_back(vector<int>());
    for (int i = 1; i <= n; ++i) if (a[i] != i) {
        ans.back().emplace_back(a[i]), ans.back().emplace_back(i);
        a[b[i]] = a[i]; b[a[i]] = a[i]; a[i] = i; b[i] = i;
    }
    if (ans.back().empty()) ans.pop_back();
    cout << ans.size() << '\n';
    for (auto& i : ans) {
        cout << (i.size() >> 1) << ' ';
        for (auto& j : i) cout << j << ' '; cout << "\n";
    }
    return 0;
}

K - Riichi!!

離譜, 比幾何過的還少, 不就到模擬嗎

就幾個函數

  1. 判斷當前狀態是否贏(枚舉哪張牌時對子, 其他的牌從小到大枚舉要么是對子要么是順子, \(O(14 * 14)\))
  2. 枚舉當前仍哪張牌, 在從小到大枚舉起到哪張牌是獲勝(調用1, O(14 * 34))

又不復雜

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define fi first
#define se second
using namespace std;

int n, m, _, cnt[4][10], a[4][10];
char s[30];
map<char, int> st;
map<int, char> ts;

void init() {
    memset(cnt, 0, sizeof cnt);
    for (int i = 2; i <= 28; i += 2) ++cnt[st[s[i]]][s[i - 1] ^ '0'];
}

bool check(int cnt[][10]) {
    memcpy(a, cnt, sizeof a);
    rep(i, 0, 3) rep(j, 1, 9) if (a[i][j]) {
        if (a[i][j] > 2) a[i][j] -= 3;
        if (i == 3 && a[i][j]) return 0;
        if (!a[i][j]) continue;
        if (j > 7 || min(a[i][j + 1], a[i][j + 2]) < a[i][j]) return 0;
        a[i][j + 1] -= a[i][j], a[i][j + 2] -= a[i][j]; a[i][j] = 0;
    }
    return 1;
}

bool win(int c[][10]) {
    rep(i, 0, 3) rep(j, 1, 9) if (c[i][j] >= 2) {
        c[i][j] -= 2;
        bool f = check(c); c[i][j] += 2;
        if (f) return 1;
    }
    return 0;
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    ts[st['w'] = 0] = 'w'; ts[st['b'] = 1] = 'b'; ts[st['s'] = 2] = 's'; ts[st['z'] = 3] = 'z';
    for (cin >> _; _; --_) {
        cin >> s + 1; init();
        if (win(cnt)) { cout << "Tsumo!\n"; continue; }
        vector<pair<pair<int, char>, vector<pair<int, char>>>> ans;
        rep(i, 0, 3) rep(j, 1, 9) if (cnt[i][j]) {
            --cnt[i][j]; ans.emplace_back(make_pair(j, ts[i]), vector<pair<int, char>>());
            rep(x, 0, 3) rep(y, 1, 9) if (x != i || y != j) {
                ++cnt[x][y];
                if (win(cnt)) ans.back().se.emplace_back(y, ts[x]);
                --cnt[x][y];
            }
            ++cnt[i][j];
            if (ans.back().se.empty()) ans.pop_back();
        }
        cout << ans.size() << "\n";
        for (auto &cur : ans) {
            cout << cur.fi.fi << cur.fi.se << ' ';
            for (auto &j : cur.se) cout << j.fi << j.se; cout << '\n';
        }
    }
    return 0;
}

L - Simone and graph coloring

逆序對而已, 自己前面比自己大的用了 k 個顏色, 拿自己就用顏色 k + 1

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair<int, int> PII;

const int N = 1e6 + 5;

int n, m, _, c[N], a[N], col[N];

void add(int x, int k) { for (; x; x -= -x & x) c[x] = max(c[x], k); }

int ask(int x) { int ans = 0; for (; x <= n; x += -x & x) ans = max(ans, c[x]); return ans; }

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    for (cin >> _ ; _; --_) {
        cin >> n; m = 0;
        for (int i = 1; i <= n; ++i) c[i] = 0, cin >> a[i];
        for (int i = 1; i <= n; ++i) {
            col[i] = ask(a[i]) + 1; m = max(m, col[i]);
            add(a[i], col[i]);
        }
        cout << m << '\n';
        for (int i = 1; i <= n; ++i) cout << col[i] << ' '; cout << '\n';
    }
    return 0;
}

M - Stone Games

主席樹板子題

從小到達枚舉加不到的數, 即

值域在\([1, k - 1]\)的數和小於 \(k - 1\), 然后\(k += sum[1, k - 1]\)

計算一下最多枚舉多少次k

int main() {
    IOS;
    for (ll ls = 0, c = 1, s = 0; c <= 1e9; ++n, s += ls + 1, ls = c, c = s + 1);
    cout << n;
    return 0;
}

42 次

不過還不放心, 打下表, 你會發現時 斐波拉契F(i) - 1

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
using ll = long long;

const int N = 1e6 + 5;

struct BIT {
    struct node { int l, r; ll val; } tr[N * 32];
    int rt[N], tot;
    void update(int& x, int y, int l, int r, int d) {
        tr[x = ++tot] = tr[y]; tr[x].val += d;
        if (l == r) return;
        int mid = l + r >> 1;
        if (mid >= d) update(tr[x].l, tr[y].l, l, mid, d);
        else update(tr[x].r, tr[y].r, mid + 1, r, d);
    }
    ll ask(int x, int y, int l, int r, int d) {
        if (l == r) return tr[x].val - tr[y].val;
        int mid = l + r >> 1;
        if (mid >= d) return ask(tr[x].l, tr[y].l, l, mid, d);
        return tr[tr[x].l].val - tr[tr[y].l].val + ask(tr[x].r, tr[y].r, mid + 1, r, d);
    }
} bit;

int n, m, k;

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    rep (i, 1, n) cin >> k, bit.update(bit.rt[i], bit.rt[i - 1], 1, 1e9, k);
    ll ls = 0;
    rep (_, 1, m) {
        ll l, r, ans = 2; cin >> l >> r; l = (l + ls) % n + 1, r = (r + ls) % n + 1;
        if (l > r) swap(r, l);
        while (1) {
            ll s = bit.ask(bit.rt[r], bit.rt[l - 1], 1, 1e9, min(ans - 1, (ll)1e9));
            if (s >= ans - 1) ans = s + 2;
            else break;
        }
        cout << (ls = ans - 1) << '\n'; 
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM