【1 级置顶】duel!

Z 为我胜,L 为 lnw 胜。

D 为 dcy 胜

第 1 场 Problem - 526C - Codeforces Z \(\textcolor{green}{2000}\)

看到感觉和之前某一道 ABC E 很相像,就是枚举红色吃多少个,然后计算代价,因为代价计算公式是有一个下取整的,猜测大体是单调的,但是局部内不是单调的,所以直接枚举前后两个段就可以了。然后发现 \(W_r\) 不像 AT 那道题一样很小,所以随便枚举 \([0, 10^6]\)\([C/W_r-10^6,C/W_r]\) 一段。

发现跑过了非常多的点,大概50多个点才wa,觉得已经和正解很近了。查看 wa 的点,发现是:

999999999 10 499999995 2 99999999

嗯,也就是说把 \(r\)\(b\) swap 一下再做一遍就可以了?

交上去就过了。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll c, hr, hb, wr, wb, ans;
ll calc(ll x) {
    ll use = x * wr;
    if(use > c) return 0;
    ll less = c - use;
    ll y = less / wb;
    ans = max(ans, x * hr + y * hb);
    return x * hr + y * hb;
}
void run(ll l, ll r) {
    l = max(l, 0LL);
    r = min(r, c);
    for(ll i = l; i <= r; i++) {
        ans = max(ans, calc(i));
    }
}
int main() {
    cin >> c >> hr >> hb >> wr >> wb;
    run(0, 1000000);
    run(c / wr - 1000000, c / wr + 1);
    run(c / wb - 1000000, c / wb + 1);
    run(wr - 1000000, wr + 1000000);
    run(wb - 1000000, wb + 1000000);
    swap(hr, hb);
    swap(wr, wb);
    run(0, 1000000);
    run(c / wr - 1000000, c / wr + 1);
    run(c / wb - 1000000, c / wb + 1);
    run(wr - 1000000, wr + 1000000);
    run(wb - 1000000, wb + 1000000);
    printf("%lld\n", ans);
}

看了题解发现和 AT 那道题什么关系都没有。考虑若吃红糖 \(i\) 颗,那么吃蓝糖 \((c-iw_r)/w_b\) 颗。所以直接根号暴力即可,红糖做 \(\sqrt c\) 个,蓝糖做 \(\sqrt c\) 个,时间复杂度 \(O(\sqrt c)\)

第 2 场 Problem - 1851F - Codeforces L \(\textcolor{orange}{1800}\)

赛时翻译软件炸掉了,把火星数的定义弄消失了,结果没看懂题……

好了不推卸责任,容易想到 01 trie,然后直接去做。

其实也可以做,但是比较麻烦。有一个简单的做法:

可以发现本题中如果两数在一位下两两不同,那么最终答案这一位下肯定为 0,否则就可以为 1,想到这里本题就很简单了,找到异或和最小的二元组即可,不过需要一点贪心处理,那就是排完序就可以线性复杂度求异或和最小的二元组了。

赛后死在异或运算优先级比比较优先级低。

#include <bits/stdc++.h>
#define N 2000010
using namespace std;
#define ll long long
ll T;
ll n, k;
struct node {
    ll idx, val;
} a[N];
void solve() {
    cin >> n >> k;
    for(int i = 1; i <= n; i ++) {
        cin >> a[i].val;
        a[i].idx = i;
    }
    sort(a + 1, a + n + 1, [](node a, node b) {
        return a.val < b.val;
    });
    // for(int i = 1; i <= n; i ++) {
    //     cerr << a[i].val << " ";
    // }
    // cerr << endl;
    ll ans = 1e18, x, y, anss;
    for(int i = 1; i < n; i ++) {
        if((a[i].val ^ a[i + 1].val) < ans) {
            ans = a[i].val ^ a[i + 1].val;
            x = a[i].idx;
            y = a[i + 1].idx;
            anss = ((1<<k) - 1) ^ (a[i].val | a[i + 1].val);
        }
    }
    cout << x << " " << y << " " << anss << endl;
}
int main() {
    cin >> T;
    while(T --) {
        solve();
    }
}

第 3 场 Problem - 1763C - Codeforces Z \(\textcolor{green}{2000}\)

lnw 没空,和 dcy 打的。

因为要上课所以看完题一个下午都没有打。

\(n\le 3\) 时暴力。当 \(n>3\) 时,假定最大值为 \(a_x\),那么 \(x\) 左右一定会有一侧个数大于 \(1\) 个,让它们进行一次操作变为一样的,然后再进行一次操作变为 \(0\),再与 \(x\) 进行一次操作变为 \(x\)。然后此时再把右边进行一次相同操作即可。也就是全部都变成了 \(a_x\),答案就是 \(na_x\)

#include <bits/stdc++.h>
#define N 200010
#define ll long long
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
using namespace std;
ll T, n, a[N], s[N], g[N], ans;
void dfs(ll x) {
    ll sum = 0;
    for(ll i = 1; i <= n; i++) {
        sum += a[i];
    }
    ans = max(ans, sum);
    if(x > n * 2) {
        return;
    }
    ll tmp[4];
    tmp[1] = a[1];
    tmp[2] = a[2];
    tmp[3] = a[3];
    for(ll i = 1; i <= n; i ++) {
        for(ll j = i + 1; j <= n; j ++) {
            ll temp = abs(a[i] - a[j]);
            for(ll k = i; k <= j; k ++) {
                a[k] = temp;
            }
            dfs(x + 1);
            for(ll k = i; k <= j; k ++) {
                a[k] = tmp[k];
            }
        }
    }
}
void solve() {
    cin >> n;
    s[0] = s[n + 1] = g[0] = g[n + 1] = 0;
    for(ll i = 1; i <= n; i++) {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
    }
    for(int i = n; i >= 1; i--) {
        g[i] = g[i + 1] + a[i];
    }
    ans = s[n];
    if(n == 2) ans = max(a[1] + a[2], abs(a[1] - a[2]) * 2);
    else if(n == 3) {
        dfs(1);
    }
    else {
        for(ll i = 1; i <= n; i++) {
            ans = max(ans, a[i] * n);
        }
    }
    printf("%lld\n", ans);
}
int main() {
    cin >> T;
    while(T --) {
        solve();
    }
}

第 4 场 Problem - 316E2 - Codeforces L \(\textcolor{orange}{2300}\)

第一眼可以看出是线段树,难在如何合并。

实际上只需要知道 \(f_{n+m}=f_{n-1}f_{m}+f_nf_{m+1}\) 就好了,斐波那契数列下标从 \(1\) 开始。

那么维护 \(\sum f_ia_i\)\(\sum f_{i-1}a_i\) 即可,线段树操作是简单的。

#include <bits/stdc++.h>
#define P 1000000000
#define N 200010
#define ll long long
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
using namespace std;
ll n, m;
ll a[N], fib[N], sum[N], lazy[N * 4];
struct node {
    // val1: fi*ai
    // val2: fi-1*ai
    ll l, r, val1, val2;
    friend node operator+(const node &x, const node &y) {
        node res;
        res.l = x.l;
        res.r = y.r;
        res.val1 = (x.val1 + fib[x.r - x.l + 2] * y.val1 + fib[x.r - x.l + 1] * y.val2) % P;
        res.val2 = (x.val2 + fib[x.r - x.l + 1] * y.val1 + fib[x.r - x.l] * y.val2) % P;
        return res;
    }
} t[N * 4];
void build(ll l, ll r, ll pos) {
    if(l == r) {
        t[pos].val1 = a[l];
        t[pos].val2 = 0;
        t[pos].l = t[pos].r = l;
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, ls(pos));
    build(mid + 1, r, rs(pos));
    t[pos] = t[ls(pos)] + t[rs(pos)];
}
void tag(ll l, ll r, ll pos, ll val) {
    (t[pos].val1 += val * sum[r - l + 1]) %= P;
    (t[pos].val2 += val * sum[r - l]) %= P;
    lazy[pos] += val;
}
void pushdown(ll l, ll r, ll pos) {
    ll mid = (l + r) >> 1;
    tag(l, mid, ls(pos), lazy[pos]);
    tag(mid + 1, r, rs(pos), lazy[pos]);
    lazy[pos] = 0;
}
void update(ll nl, ll nr, ll l, ll r, ll pos, ll val) {
    if(nl <= l && r <= nr) {
        tag(l, r, pos, val);
        return;
    }
    if(lazy[pos]) {
        pushdown(l, r, pos);
    }
    int mid = (l + r) >> 1;
    if(nl <= mid) {
        update(nl, nr, l, mid, ls(pos), val);
    }
    if(nr > mid) {
        update(nl, nr, mid + 1, r, rs(pos), val);
    }
    t[pos] = t[ls(pos)] + t[rs(pos)];
}
void update(ll x, ll l, ll r, ll pos, ll val) {
    if(l == r) {
        t[pos].val1 = val;
        t[pos].val2 = 0;
        return;
    }
    if(lazy[pos]) {
        pushdown(l, r, pos);
    }
    int mid = (l + r) >> 1;
    if(x <= mid) {
        update(x, l, mid, ls(pos), val);
    }
    else {
        update(x, mid + 1, r, rs(pos), val);
    }
    t[pos] = t[ls(pos)] + t[rs(pos)];
}
node query(ll nl, ll nr, ll l, ll r, ll pos) {
    if(nl <= l && r <= nr) {
        return t[pos];
    }
    if(lazy[pos]) {
        pushdown(l, r, pos);
    }
    int mid = (l + r) >> 1;
    node res;
    bool flag = 0;
    if(nl <= mid) {
        res = query(nl, nr, l, mid, ls(pos));
        flag = 1;
    }
    if(nr > mid) {
        if(flag) {
            res = res + query(nl, nr, mid + 1, r, rs(pos));
        } else {
            res = query(nl, nr, mid + 1, r, rs(pos));
        }
    }
    return res;
}
int main() {
    scanf("%lld %lld", &n, &m);
    fib[1] = fib[2] = 1;
    for(ll i = 3; i <= n; i ++) {
        fib[i] = (fib[i - 1] + fib[i - 2]) % P;
    }
    for(ll i = 1; i <= n; i ++) {
        sum[i] = (sum[i - 1] + fib[i]) % P;
    }
    for(ll i = 1; i <= n; i ++) {
        scanf("%lld", &a[i]);
    }
    build(1, n, 1);
    while(m --) {
        ll opt;
        scanf("%lld", &opt);
        if(opt == 1) {
            ll x, v;
            scanf("%lld %lld", &x, &v);
            update(x, 1, n, 1, v);
        } else if(opt == 2) {
            ll l, r;
            scanf("%lld %lld", &l, &r);
            node res = query(l, r, 1, n, 1);
            printf("%lld\n", res.val1);
        } else {
            ll l, r, v;
            scanf("%lld %lld %lld", &l, &r, &v);
            update(l, r, 1, n, 1, v);
        }
    }
}

第 5 场 Problem - B - Codeforces Z \(\textcolor{green}{2200}\)

再也不和陌生人打 duel 了。

就是给出若干个先序遍历或者后序遍历,那么它们的根的前后数的集合肯定一样,要么就完全相反。

所以可以用递归 \(solve(l,r)\) 表示处理 \([l,r]\) 的区间内的数,那么枚举这个区间的根节点 \(x\),只需要保证 \([l,x)\)\((x,r]\) 对于所有遍历的数集相同或者相反即可,可以用哈希实现。

如果数集相反,记得在递归前把它们转回来。

总共递归 \(n\) 层,每层 \(n\) 个数,枚举遍历是 \(k\) 的,那么就是 \(O(n^2k)\)

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
ull a[110][1000], ans[1000], b[110][1000], c[1000], d[110][1000], tmp[1000];
mt19937_64 rnd(chrono::steady_clock::now().time_since_epoch().count());
ull n, k;
void solve(ull l, ull r, ull fa) {
    if(l == r) {
        ans[a[1][l]] = fa;
        return ;
    }
    for(ull i = l; i <= r; i ++) {
        ull pos = d[1][a[1][i]];
        ull pre = b[1][pos - 1] ^ b[1][l - 1];
        ull suf = b[1][r] ^ b[1][pos];
        bool flag = 1;
        if(pre == 0 || suf == 0) flag = 0;
        // 枚举根
        for(ull j = 2; j <= k; j ++) {
            ull poss = d[j][a[1][i]];
            ull ppre = b[j][poss - 1] ^ b[j][l - 1];
            ull ssuf = b[j][r] ^ b[j][poss];
            if(ppre == pre && ssuf == suf);
            else if(ppre == suf && ssuf == pre);
            else flag = 0;
        }
        // right
        if(flag) {
            for(ull j = 2; j <= k; j ++) {
                ull poss = d[j][a[1][i]];
                ull ppre = b[j][poss - 1] ^ b[j][l - 1];
                ull ssuf = b[j][r] ^ b[j][poss];
                if(ppre == suf && ssuf == pre) {
                    ll len = 0;
                    for(ull u = poss + 1; u <= r; u ++) {
                        tmp[++ len] = a[j][u];
                    }
                    tmp[++ len] = a[j][poss];
                    for(ull u = l; u < poss; u ++) {
                        tmp[++ len] = a[j][u];
                    }
                    len = 0;
                    for(ull u = l; u <= r; u ++) {
                        a[j][u] = tmp[++ len];
                        b[j][u] = b[j][u - 1] ^ c[a[j][u]];
                        d[j][a[j][u]] = u;
                    }
                }
            }
            ans[a[1][i]] = fa;
            solve(l, pos - 1, a[1][i]);
            solve(pos + 1, r, a[1][i]);
            return;
        }
    }
}
int main() {
    scanf("%d %d", &n, &k);
    for(ull i = 1; i <= n; i ++) c[i] = rnd();
    for(ull i = 1; i <= k; i ++) {
        for(ull j = 1; j <= n; j ++) {
            scanf("%d", &a[i][j]);
            b[i][j] = b[i][j - 1] ^ c[a[i][j]];
            d[i][a[i][j]] = j;
        }
    }
    solve(1, n, -1);
    for(ull i = 1; i <= n; i ++) {
        printf("%d ", ans[i]);
    }
}

第 6 场 Problem - 785E - Codeforces Z \(\textcolor{green}{2200}\)

还是和陌生人打了一把 duel。

可以考虑分块,每个块用树状数组维护权值。然后交换两个数相当于先把它们删掉再添加,互相计算即可。

#include <bits/stdc++.h>
#define N 200010
#define ll long long
using namespace std;
int n, q, b;
ll ans;
int a[N], idx[N];
int t[500][N];
int L[500], R[500];
void upd(int t[], int x, int v) {
    for(int i = x; i <= n; i += i & -i) t[i] += v;
}
int qry(int t[], int x) {
    int res = 0;
    for(int i = x; i > 0; i -= i & -i) res += t[i];
    return res;
}
int main() {
    scanf("%d%d", &n, &q);
    b = sqrt(n * 17);
    for(int i = 1; i <= n; i ++) {
        a[i] = i;
        idx[i] = (i - 1) / b + 1;
        if(!L[idx[i]]) L[idx[i]] = i;
        R[idx[i]] = i;
    }
    for(int i = 1; i <= n; i ++) {
        upd(t[idx[i]], i, 1);
    }
    for(int i = 1; i <= q; i ++) {
        int l, r;
        scanf("%d%d", &l, &r);
        if(l == r) {
            printf("%lld\n", ans);
            continue;
        }
        if(l > r) swap(l, r);
        for(int j = 1; j < idx[l]; j ++) {
            ans -= qry(t[j], n) - qry(t[j], a[l]);
        }
        for(int j = idx[l] + 1; j <= idx[n]; j ++) {
            ans -= qry(t[j], a[l]);
        }
        for(int j = L[idx[l]]; j < l; j ++) {
            ans -= (a[j] > a[l]);
        }
        for(int j = l + 1; j <= R[idx[l]]; j ++) {
            ans -= (a[j] < a[l]);
        }
        for(int j = 1; j < idx[r]; j ++) {
            ans -= qry(t[j], n) - qry(t[j], a[r]);
        }
        for(int j = idx[r] + 1; j <= idx[n]; j ++) {
            ans -= qry(t[j], a[r]);
        }
        for(int j = L[idx[r]]; j < r; j ++) {
            ans -= (a[j] > a[r]);
        }
        for(int j = r + 1; j <= R[idx[r]]; j ++) {
            ans -= (a[j] < a[r]);
        }
        ans += (a[l] > a[r]);

        upd(t[idx[l]], a[l], -1);
        upd(t[idx[r]], a[r], -1);
        swap(a[l], a[r]);
        upd(t[idx[l]], a[l], 1);
        upd(t[idx[r]], a[r], 1);

        for(int j = 1; j < idx[l]; j ++) {
            ans += qry(t[j], n) - qry(t[j], a[l]);
        }
        for(int j = idx[l] + 1; j <= idx[n]; j ++) {
            ans += qry(t[j], a[l]);
        }
        for(int j = L[idx[l]]; j < l; j ++) {
            ans += (a[j] > a[l]);
        }
        for(int j = l + 1; j <= R[idx[l]]; j ++) {
            ans += (a[j] < a[l]);
        }
        for(int j = 1; j < idx[r]; j ++) {
            ans += qry(t[j], n) - qry(t[j], a[r]);
        }
        for(int j = idx[r] + 1; j <= idx[n]; j ++) {
            ans += qry(t[j], a[r]);
        }
        for(int j = L[idx[r]]; j < r; j ++) {
            ans += (a[j] > a[r]);
        }
        for(int j = r + 1; j <= R[idx[r]]; j ++) {
            ans += (a[j] < a[r]);
        }
        ans -= (a[l] > a[r]);

        printf("%lld\n", ans);
    }
}

第 6 场 Problem - 1765F - Codeforces 都不会 \(\textcolor{red}{2200}\)

我填 2000~2200,lnw 填 2200~2500,于是生成了一道 2200 的题。

结果感觉应该有 2700 吧。好难想。

首先考虑恰好选两个的时候 \((x_1,c_1),(x_2,c_2)\),设两个分别取了 \((a_1,a_2)\),希望浓度为 \(x\),解一个方程:

\[ \begin{cases} a_1+a_2=1 \\ x_1a_1+x_2a_2=x \end{cases} \]

那么发现当 \(x=x_1\)\(a_1=1,a_2=0\),代价为 \(c_1\),当 \(x=x_2\)\(a_1=0,a_2=1\),代价为 \(c_2\)

其实发现代价就是连接 \((x_1,c_1)\)\((x_2,c_2)\) 两个点的一个一次函数。

那么期望值就是这个这个一次函数,与 \(x=x_1\)\(x=x_2\) 两条直线和 \(x\) 轴围成的梯形积起来,也就是:

\[ \dfrac{(c_1+c_2)(x_1-x_2)}{2} \]

考虑更多点时,代价就是这些点围成的凸包,那么期望值也是这个凸包与两端垂直于 \(x\) 轴的直线与 \(x\) 轴围成的图形的面积了。

那么我们可以用 \(O(n^3)\) 维护这个凸包。考虑瓶颈是什么,就是我们要记录上一条线的斜率。但是实际上我们发现当有下面这种非凸包的情况时,一定不优(面积不比凸包大):

所以不需要记录上一条线的斜率,dp 最优时就是凸包。

所以有:

\[ f_i=-w_i+f_j+g(j,i) \]

其中 \(g(j,i)\)\(j\)\(i\) 围成的面积。

时间复杂度 \(O(n^2)\)

#include <bits/stdc++.h>
#define int long long
#define N 5010
#define db long double
using namespace std;
int n, k;
db f[N], ans;
struct node {
    int x, w, c;
} a[N];
signed main() {
    cin >> n >> k;
    for(int i = 1; i <= n; i ++) {
        cin >> a[i].x >> a[i].w >> a[i].c;
    }
    sort(a + 1, a + n + 1, [](node a, node b) {
        return a.x < b.x;
    });
    for(int i = 1; i <= n; i ++) {
        f[i] = -a[i].w;
        for(int j = 1; j < i; j ++) {
            f[i] = max(f[i], - a[i].w + f[j] + 1.0 * (a[i].x - a[j].x) * (a[i].c + a[j].c) / 200 * k);
        }
        ans = max(ans, f[i]);
    }
    cout << fixed << setprecision(10) << ans << endl;
}

本文作者:ZnPdCo

本文链接: https://znpdco.github.io/blog/2024/10/29/duel%EF%BC%81/

本页面的全部内容在 CC BY-SA 4.0SATA 协议之条款下提供,附加条款亦可能应用

评论