zoukankan      html  css  js  c++  java
  • 第 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 << '
    ';
        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] << '
    ';
        }
        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
    " : "HUMAN
    ");
        }
        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 << '
    ';
        }
        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 << '
    '; }   
    };
    
    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
    "; 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() << '
    ';
        for (auto& i : ans) {
            cout << (i.size() >> 1) << ' ';
            for (auto& j : i) cout << j << ' '; cout << "
    ";
        }
        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!
    "; 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() << "
    ";
            for (auto &cur : ans) {
                cout << cur.fi.fi << cur.fi.se << ' ';
                for (auto &j : cur.se) cout << j.fi << j.se; cout << '
    ';
            }
        }
        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 << '
    ';
            for (int i = 1; i <= n; ++i) cout << col[i] << ' '; cout << '
    ';
        }
        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) << '
    '; 
        }
        return 0;
    }
    
  • 相关阅读:
    大道至简第二篇阅读笔记
    大道至简第一篇阅读笔记
    冲刺第十天
    冲刺第九天
    冲刺第八天
    冲刺第七天
    用java构造一个带层次的文件目录遍历器
    用java进行简单的万年历编写
    delphi 图像处理 图像左旋右旋
    delphi 图像处理 图像放大缩小
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/14624171.html
Copyright © 2011-2022 走看看