第 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;
}
posted @ 2021-04-06 22:42  洛绫璃  阅读(822)  评论(0编辑  收藏  举报