Codeforces Round #709 (Div. 2, based on Technocup 2021 Final Round)

Codeforces Round #709 (Div. 2, based on Technocup 2021 Final Round)

A - Prison Break

int main() {
    IOS;
    for (cin >> _; _; --_) {
        ll n, m; cin >> n >> m;
        cout << (m - 1) * n + n << '\n';
    }
    return 0;
}

B - Restore Modulo

\(a_i - a_{i - 1}\) 注意到只有两种结果

  1. \(a_i - a_{i - 1} < 0\) 其值为 \(c - m\)
  2. \(a_i - a_{i - 1} \geqslant 0\) 其值为 \(c\)

\(a_i - a_{i - 1}\) 值大于2个无解, 有两个值且不是一个大于等于0 小于0 无解

\(a_i - a_{i - 1}\) 值有一个, m无限大

剩下的直接算出来\(c, m\) 再去特判 \(m \leqslant a_i\) 即可

ll a[N];
 
int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> a[1]; set<ll> st; ll m, c;
        rep(i, 2, n) cin >> a[i], st.insert(a[i] - a[i - 1]);
        if (st.size() > 2) { cout << "-1\n"; continue; }
        else if (st.size() == 2 && *st.rbegin() < 0) { cout << "-1\n"; continue; }
        else if (st.size() == 2 && *st.begin() >= 0) { cout << "-1\n"; continue; }
        else if (st.size() == 2) m = *st.rbegin() - *st.begin(), c = *st.rbegin();
        else if (st.size() <= 1) { cout << "0\n"; continue; }
        bool f = a[1] < m;
        rep (i, 2, n) if (a[i] >= m || a[i] != (a[i - 1] + c) % m) f = 0;
        if (f) cout << m << ' ' << c << '\n';
        else cout << "-1\n";
    }
    return 0;
}

C - Basic Diplomacy

注意到每个数能用\(\left \lceil \frac{m}{2} \right \rceil\)

故在处理掉某天只能选一个人的情况(有解的情况下), 必定有解

int c[N], ans[N];
set<int> b[N];
 
int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m; bool f = 1;
        rep(i, 1, n) c[i] = (m + 1) >> 1;
        rep(i, 1, m) {
            ans[i] = 0; clear(b[i]);
            for (cin >> k; k; --k) cin >> cas, b[i].insert(cas);
            if (b[i].size() == 1) ans[i] = *b[i].begin(), --c[*b[i].begin()];
        }
        rep(i, 1, m) if (!ans[i]) {
            if (c[*b[i].begin()] > 0) --c[*b[i].begin()], ans[i] = *b[i].begin();
            else --c[*b[i].rbegin()], ans[i] = *b[i].rbegin();
        }
        rep (i, 1, n) if (c[i] < 0) f = 0;
        if (!f) { cout << "NO\n"; continue; }
        cout << "YES\n"; rep(i, 1, m) cout << ans[i] << ' '; cout << '\n';
    }
    return 0;
}

D - Playlist

两个循环队列 a, b, a为原音乐的循环队列, b维护可能删除b[i]这首歌在原队列a中下一首歌的序列

循环检测b[i]是否能删除祁在原序列中的下一首歌

如果能, 就把a中的那首歌删除, 如果删除的歌也在b序列, 那再b序列中也删除

否则在b序列删除当前b[i]

对于每首歌, 要么b[i]的时候被删除, 或者b[i]本身被删除, 总的每首歌都被从b序列删除之后停止

故复杂度为 \(O(n)\)

struct node { int pre, nxt, k; } a[N], b[N];

int n, m, _, k, cas;

void delet(node* a, int id) {
    a[a[id].nxt].pre = a[id].pre, a[a[id].pre].nxt = a[id].nxt;
}

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n; map<int, int> st; m = 0; VI ans;
        rep(i, 1, n) {
            cin >> a[i].k, b[i].pre = a[i].pre = i - 1;
            b[i].k = i; b[i].nxt = a[i].nxt = i + 1;
        }
        b[1].pre = a[1].pre = n, b[n].nxt = a[n].nxt = 1;
        for (int i = 1; ; i = b[i].nxt)
            if (__gcd(a[b[i].k].k, a[a[b[i].k].nxt].k) == 1) {
                int bnxt = b[i].nxt;
                if (b[bnxt].k == a[b[i].k].nxt) delet(b, bnxt);
                ans.pb(a[b[i].k].nxt); delet(a, a[b[i].k].nxt);
                if (i == b[bnxt].k && b[bnxt].k == a[b[i].k].nxt) break;
            }
            else { delet(b, i); if (b[i].nxt == i) break; }
        cout << ans.size() << ' ';
        for (auto& i : ans) cout << i << ' '; cout << '\n';
    }
    return 0;
}

E Skyline Photo

一个单调栈即可, 因为要建筑要连续, 且只有在 b[i] 在当前序列中最低才有贡献

故维护一个单调栈, h[st.top()] > h[i] i可以和 st.top() 合并成一个序列, 且这段序列魅力值为 b[i]

为了dp, 我们还要再维护一个mx[i] 表示 不含 当前 i 这一序列的最大值, 则

f[i] = mx[i] + b[i]

对于 mx[i], 首先是 mx[i] = f[i - 1]

当 h[st.top()] > h[i] 时, i 可以合并, 则 mx[i] = max(mx[i], mx[st.top])

最后是在 h[st.top()] < h[i] 时, 还有一种选择, b[i] 没贡献 i, f[i] = max(f[i], f[st.top()])

const int N = 3e5 + 5;

int n, m, _, k, cas;
int h[N], b[N];
ll f[N], mx[N];

int main() {
    IOS; cin >> n; stack<int> st;
    rep(i, 1, n) cin >> h[i];
    rep(i, 1, n) cin >> b[i];
    rep(i, 1, n) {
        for (mx[i] = f[i - 1]; !st.empty() && h[st.top()] > h[i]; st.pop())
            umax(mx[i], mx[st.top()]);
        f[i] = b[i] + mx[i];
        if (!st.empty()) umax(f[i], f[st.top()]); st.push(i);
    }
    cout << f[n];
    return 0;
}

F - Useful Edges

注意 5s 的时限

意味着\(O(n)\)跑了, 边和询问都是 \(n^2\) 的数量级, 也就是 \(O(n\times m + n \times q)\) 的复杂度是允许的

先弗洛伊德跑个最短路, 毕竟这是个求最短路的问题

对于每个询问 \((u, v, c)\), 好边是 \(d[u][x] + cost(x, y) + d[y][v] <= c\)

暴力的 \(O(m \times q)\) 是不行了, 只能想办法 \(O(n\times m + n \times q)\)

那就只能通过遍历 顶点 i, 来解决\((i, v, c)\)的询问了

对每个顶点预处理个 \(g[y] = max(c_k - d[y][v_k])\) 即枚举边的时候我们只用考虑 \(d[i][x] + cost(x, y)\)

memset(g, -1, sizeof g);
for (auto &[y, c] : h[i]) rep (j, 1, n) umax(g[j], c - d[j][y]);
rep (j, 1, m) if (d[i][e[j].u] + e[j].c <= g[e[j].v]) vis[j] = 1;

完整代码

struct node { int u, v, c; } e[N];

int n, m, _, k, cas;
vector<PII> h[M];
ll d[M][M], g[M];
bool vis[N];

int main() {
    IOS; cin >> n >> m; memset(d, 0x3f, sizeof d);
    rep (i, 1, n) d[i][i] = 0;
    rep (i, 1, m) {
        cin >> e[i].u >> e[i].v >> e[i].c;
        umin(d[e[i].u][e[i].v], e[i].c); d[e[i].v][e[i].u] = d[e[i].u][e[i].v];
    }
    rep (k, 1, n) rep (i, 1, n) rep (j, 1, n) umin(d[i][j], d[i][k] + d[k][j]);
    for (cin >> _; _; --_) { int u, v, c; cin >> u >> v >> c; h[u].pb(v, c); h[v].pb(u, c); }
    rep (i, 1, n) {
        memset(g, -1, sizeof g);
        for (auto &[y, c] : h[i]) rep (j, 1, n) umax(g[j], c - d[j][y]);
        rep (j, 1, m) if (d[i][e[j].u] + e[j].c <= g[e[j].v]) vis[j] = 1;
    }
    rep (i, 1, m) k += vis[i]; cout << k;
    return 0;
}
posted @ 2021-03-22 15:17  洛绫璃  阅读(60)  评论(0编辑  收藏  举报