AtCoder Beginner Contest 368

A

void solve(){
    int k;
    cin >> n >> k;
    for(int i = 1; i <= n; i ++){
        cin >> in[i];
    }
    for(int i = n - k + 1; i <= n; i ++) cout << in[i] << ' ';
    for(int i = 1; i <= n - k; i ++) cout << in[i] << ' ';
}

B

模拟

void solve(){
    cin >> n;
    for(int i = 1; i <= n; i ++){
        cin >> in[i];
    }
    int s = 0;
    while(true){
        sort(in + 1, in + n + 1, [](int a, int b){
            return a > b;
        });
        if(in[1] == 0 || in[2] == 0) break;
        in[1] --;
        in[2] --;
        s ++;
    }
    cout << s << '\n';
}

C

发现对于对于每个怪物的伤害与轮数成正相关,二分。

void solve(){
    int res = 0; // 已经经过的进攻回合
    cin >> n;
    auto f = [&] (int x) -> int {
        if(res % 3 == 0){
            return x / 3 * 5 + x % 3;
        }
        if(res % 3 == 1){
            if(x == 1) return 1;
            if(x == 2) return 4;
            x -= 2;
            return 4 + x / 3 * 5 + x % 3;
        }
        if(res % 3 == 2){
            if(x == 1) return 3;
            -- x;
            return 3 + x / 3 * 5 + x % 3;
        }
        return 0;
    };
    // cout << f(4) << '\n';
    for(int i = 1; i <= n; i ++){
        cin >> q;
        int l = 1, r = 1e9;
        while(l < r){    
            int mid = l + r >> 1;
            if(f(mid) >= q) r = mid;
            else l = mid + 1;
        }
        // cout << i << ' ' << l << '\n';
        res += l;
    }
    cout << res;
    // int l = 0, r = 1e18;
    // while(l < r){
        // int mid = l + r >> 1;
        // if(mid / 3 * 3 + mid % 3 >= s) r = mid;
        // else l = mid + 1;
    // }
    // cout << l;
}

D

sol1

只需要将每一对相邻点 u u u v v v 的最短路径的边全部加 1 1 1 即可,最后统计所有边权 ≥ 1 \geq 1 1 的边的数量。

使用重链剖分完成。(有点小题大做)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lc u << 1
#define rc u << 1 | 1
int const N = 2e5 + 10;
std::vector<int> e[N];
int sz[N], son[N], dep[N], fa[N];
int w[N], nw[N], id[N], top[N], tim = 0;

void dfs1(int u, int father){
    sz[u] = 1, dep[u] = dep[father] + 1, fa[u] = father;
    for(int v : e[u]){
        if(v == father) continue ;
        dfs1(v, u);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]]) son[u] = v;
    }
}
// id : 存 u 剖分之后的编号
// nw : 新编号在树中对应的权值
void dfs2(int u, int tp){
    top[u] = tp, id[u] = ++ tim, nw[tim] = w[u];
    if(!son[u]) return ;
    dfs2(son[u], tp);
    for(int v : e[u]){
        if(v == fa[u] || v == son[u]) continue ;
        dfs2(v, v);
    }
}

struct tree{
    int l, r;
    int sum, add;
}tr[N * 4];

void pushup(int u){
    tr[u].sum = tr[lc].sum + tr[rc].sum;
}

void pushdown(int u){
    if(tr[u].add){
        tr[lc].sum += tr[u].add * (tr[lc].r - tr[lc].l + 1);
        tr[rc].sum += tr[u].add * (tr[rc].r - tr[rc].l + 1);
        tr[lc].add += tr[u].add;
        tr[rc].add += tr[u].add;
        tr[u].add = 0;
    }
}

void build(int u, int l, int r){
    tr[u] = {l, r, nw[l], 0};
    if(l == r) return ;
    int mid = l + r >> 1;
    build(lc, l, mid);
    build(rc, mid + 1, r);
    pushup(u);
}

void update(int u, int l, int r, int k){
    if(l <= tr[u].l && r >= tr[u].r){
        tr[u].sum += k * (tr[u].r - tr[u].l + 1);
        tr[u].add += k;
        return ;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    pushdown(u);
    if(l <= mid) update(lc, l, r, k);
    if(r > mid) update(rc, l, r, k);
    pushup(u);
}

// 将树从 u 到 v 节点最短路径上的所有节点的值加上 k
void update_path(int u, int v, int k){
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        update(1, id[top[u]], id[u], k);
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u, v);
    update(1, id[v], id[u], k);
}

// 以 u 为根的子树的所有节点的值加 k
// 以 u 为根的子树的 dfs 序是连续的
void update_tree(int u, int k){
    update(1, id[u], id[u] + sz[u] - 1, k);
}

int ask(int u, int l, int r){
    if(l <= tr[u].l && r >= tr[u].r){
        return tr[u].sum;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    pushdown(u);
    int res = 0;
    if(l <= mid) res += ask(lc, l, r);
    if(r > mid) res += ask(rc, l, r);
    pushup(u);
    return res;
}

// 查询树从 u 到 v 节点最短路径上的所有节点的值之和
int ask_path(int u, int v){
    int res = 0;
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        res += ask(1, id[top[u]], id[u]);
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u, v);
    res += ask(1, id[v], id[u]);
    return res;
}

// 查询以 u 为根的子树的节点值之和
int ask_tree(int u){
    return ask(1, id[u], id[u] + sz[u] - 1);
}

int lca(int u, int v){
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        u = fa[top[u]];
    }
    return dep[u] < dep[v] ? u : v;
}

int n, k, m, q, a, b, c;

int in[N];



int fg[N], dp[N];

// fg : 是否需要并到答案集合
// dp : 以 i 为根子树达成要求的最小花费

int v[N];
set<int> sp;

void dfs1(int u, int fa, int up){
    if(sp.count(u)) fg[u] = 1;
    int s = 0;
    for(auto &v : e[u]){
        if(v == fa) continue ;
        dfs1(v, u);
        if(fg[v]) s ++;
    }
    if(s >= 2) fg[u] = 1;
}
// int dfs(int u, int fa){
//     if(fg[u] == 0) return dp[u] = 0;
//     dp[u] = 1;
//     for(auto &v : e[u]){
//         if(v == fa) continue ;
//         dfs(v, u);
//         if(fg[v]) dp[u] += dp[v];
//     }
//     return dp[u];
// }
// int p[N];
// int find(int u){
//     return p[u] == u ? u : p[u] = find(p[u]);
// }
void solve(){
    cin >> n >> k;
    // for(int i = 1; i <= n; i ++){
    //     cin >> p[i];
    // }
    for(int i = 1; i < n; i ++){
        cin >> a >> b;
        e[a].push_back(b);
        e[b].push_back(a);
    }
    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);
    for(int i = 1; i <= k; i ++){
        cin >> v[i];
        if(i >= 2){
            update_path(v[i], v[i - 1], 1);
            int Lca = lca(v[i], v[i - 1]);
            update_path(Lca, Lca, -1);
        }
        // sp.insert(v[i]);
    }
    int res = 0;
    for(int i = 1; i <= n; i ++){
        for(auto &j : e[i]){
            if(j > i){
                int Lca = lca(i,j);
                if(ask_path(i, j) - ask_path(Lca,Lca)>=1){
                    res ++;
                }
            }
        }    
    }
    res ++;
    cout <<res;// max(res,0LL);
    // for(int i = 1; )
    // dfs1(1, 0);
    // cout << dfs(1, 0) << '\n';
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T = 1;
    while (T --){
        solve();
    }

    return 0;
}

sol2

发现以随意一个需要选择的点以根节点。

这样形态的树,对于根节点的每个链,最深的被需要的儿子下面的都不选即可。

int n, k;
vector<int> p;
vector<int> e[N];
int fg[N];

void dfs(int u, int fa){
    for(auto &v : e[u]){
        if(v == fa) continue ;
        dfs(v, u);
        fg[u] |= fg[v];
    }
}

void solve(){
    cin >> n >> k;
    for(int i = 1; i < n; i ++){
        int a, b;
        cin >> a >> b;
        e[a].push_back(b);
        e[b].push_back(a);
    }
    for(int i = 1; i <= k; i ++){
        int x;
        cin >> x;
        fg[x] = 1;
        p.push_back(x);
    }
    dfs(p[0], 0);
    cout << accumulate(fg + 1, fg + n + 1, 0LL) << '\n';
}

E

参考 jiangly 的代码。

对于 ∀ j \forall j j ,需要找到以下 i i i :

b i = a j , t i ≤ s j b_i=a_j,t_i\leq s_j bi=ajtisj

它们需要满足 :

t i + x t ≤ s j + x j t_i+x_t\leq s_j+x_j ti+xtsj+xj ,即 x j ≥ t i + x i − s j x_j\geq t_i+x_i-s_j xjti+xisj

那么为了让 x x x 最小, x x x 取 $\max _{b_i=a_j,t_i\leq s_j}{t_i+x_i-s_j} $ 即可。

按照时间排序,对于每个 s s s 要处理询问,对于每个 t t t 维护 t + x t+x t+x 的最值即可。

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

using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N, M, X0;
    cin >> N >> M >> X0;
    
    vector<int> A(M), B(M), S(M), T(M);
    for (int i = 0; i < M; i++) {
        cin >> A[i] >> B[i] >> S[i] >> T[i];
        A[i]--;
        B[i]--;
    }
    
    vector<array<int, 3>> e(2 * M);
    for (int i = 0; i < M; i++) {
        e[2 * i] = {S[i], 1, i}; // 查询用 1
        e[2 * i + 1] = {T[i], 0, i}; // 更新用 0
    }
    
    sort(e.begin(), e.end());
 
    vector<int> X(M);
    vector<int> tm(N); // 表示 b[i] 的最大的 T[i] + X[i]
    X[0] = X0;
    for (auto [t, o, i] : e) {
        if (o == 1) { // 查询 X[i]
            X[i] = max(X[i], tm[A[i]] - S[i]);
        } else { // 
            tm[B[i]] = max(tm[B[i]], T[i] + X[i]);
        }
    }
    
    for (int i = 1; i < M; i++) {
        cout << X[i] << " \n"[i == M - 1];
    }
    
    return 0;
}

F

F 是一个公平组合游戏,而且总状态有限,每个状态的后继状态在 O ( x ) O(\sqrt x) O(x ) 时间内即可枚举完。

所以使用 sg 定理求解

int n;

int a[N];
int f[N];

int sg(int x){
    if(f[x] != -1) return f[x];
    unordered_set<int> q;
    for(int i = 1; i <= x / i; i ++){
        if(x % i == 0){
            if(x / i != x) q.insert(sg(x / i));
            if(i != x) q.insert(sg(i));
        }
    }
    for(int i = 0; ; i ++){
        if(!q.count(i)) return f[x] = i;
    }
}

void solve(){
    cin >> n;
    for(int i = 1; i <= n; i ++){
        cin >> a[i];
    }
    memset(f, -1, sizeof f);
    int res = 0;
    for(int i = 1; i <= n; i ++){
        res ^= sg(a[i]);
    }

    cout << (res ? "Anna" : "Bruno") << '\n';
}

G

特别需要注意到题目保证第三种询问的答案不会超过 1 0 18 10^{18} 1018 ,又因为 2 59 < 1 0 18 < 2 60 2^{59}<10^{18}<2^{60} 259<1018<260

所以选择 b b b 的次数一定不会超过 60 60 60 次。

因此对于每个询问,可以选择的 b b b 及其有限。

我们用一个 s e t set set 维护所有 b i ≠ 2 b_i\neq 2 bi=2 的位置,用 B I T BIT BIT 维护 a a a 的区间和。

那么对于每个询问,查询出来所有决策点,即 b i ≥ 2 b_i\geq 2 bi2 的位置,对于 b i = 1 b_i=1 bi=1 的位置,一定选择加法的,对于所有决策点,选择更优的。

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

int const N = 1e5 + 10;

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

int tr[N];

void add(int p, int v){
    for(int i = p; i < N; i += i & -i){
        tr[i] += v;
    }
}

int ask(int p){
    int res = 0;
    for(int i = p; i; i -= i & -i){
        res += tr[i];
    }
    return res;
}

int ask(int l, int r){
    if(l > r) return 0;
    return ask(r) - ask(l - 1);
}

set<int> pos;

void solve(){
    cin >> n;
    for(int i = 1; i <= n; i ++){
        cin >> a[i];
        add(i, a[i]);
    }
    for(int i = 1; i <= n; i ++){
        cin >> b[i];
        if(b[i] > 1){
            pos.insert(i);
        }
    }
    cin >> q;
    while(q --){
        int opt, l, r;
        cin >> opt >> l >> r;
        if(opt == 1){
            add(l, r - a[l]);
            a[l] = r;
        }
        else if(opt == 2){
            if(r == b[l]) continue ;
            if(b[l] == 1){ // 1 变非 1
                pos.insert(l);
            }
            if(r == 1){
                pos.erase(l);
            }
            b[l] = r;
        }
        else{
            int res = a[l];
            if(l != r){
                l ++;
                vector<int> p;
                auto it = pos.lower_bound(l);
                if(it != pos.end()){
                    while((*it) <= r && it != pos.end()){
                        p.push_back(*it);
                        it ++;
                    }
                }    
                // cout << "Pos: \n";
                // for(auto &ps : p) cout << ps << ' '; cout << '\n';
                if(p.size() == 0){
                    res += ask(l, r);
                }
                else{
                    res += ask(l, p[0] - 1);
                    res = max(res * b[p[0]], res + a[p[0]]);
                    // res *= b[p[0]];
                    for(int i = 0; i + 1 < p.size(); i ++){
                        res += ask(p[i] + 1, p[i + 1] - 1);
                        res = max(res * b[p[i + 1]], res + a[p[i + 1]]);
                        // res *= b[p[i + 1]];
                    }
                    res += ask(p.back() + 1, r);
                }
            }
            cout << res << '\n';
        }
    }
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0); 
    solve();
    return 0;
}
  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值