首页 > 其他 > 详细

LibreOJ 题解汇总

时间:2019-02-20 22:14:22      阅读:381      评论:0      收藏:0      [点我收藏+]

LibreOJ 是一个很好的 OJ,从现在(2019 年 2 月 20 日)开始我的刷题重心将从洛谷逐渐转向 LibreOJ。

这里按照编号排列列出我做过的题目的题解。

您可以通过浏览器的搜索功能找到对应题目。

#1. A + B Problem

最初的一题。

#include <cstdio>

int a, b;

int main() {
    scanf("%d%d", &a, &b);
    printf("%d\n", a + b);
    return 0;
}

#2. Hello, World!

你好,世界!

#include <cstdio>

int main() {
    puts("Hello, World!");
    return 0;
}

#3. Copycat

人类的本质之一。

#include <cstdio>

const int MN = 1005;

int T;
char str[MN];

int main() {
    freopen("copycat.in", "r", stdin);
    freopen("copycat.out", "w", stdout);
    scanf("%d", &T);
    for (int i = 1; i <= T; ++i) {
        scanf("%s", str);
        printf("%s\n", str);
    }
    return 0;
}

#101. 最大流

采用带有当前弧优化和多路增广的 Dinic 算法解决最大流问题。

Dinic 算法在一般图的最坏时间复杂度为 \(O(n^2m)\)

#include <cstdio>
#include <cstring>
#include <algorithm>

typedef long long LL;
const int MN = 105, MM = 5005;
const LL Inf = 0x7fffffffffffffff;

int N, M, S, T;
int h[MN], iter[MN], nxt[MM * 2], to[MM * 2], tot = 1; LL w[MM * 2];
inline void ins(int u, int v, LL x) {
    nxt[++tot] = h[u], to[tot] = v, w[tot] = x, h[u] = tot;
}

int lv[MN], que[MN], l, r;

inline bool Lvl() {
    memset(lv, 0, sizeof(lv));
    lv[S] = 1;
    que[l = r = 1] = S;
    while (l <= r) {
        int u = que[l++];
        for (int i = h[u]; i; i = nxt[i]) if (w[i] && !lv[to[i]]) {
            lv[to[i]] = lv[u] + 1;
            que[++r] = to[i];
        }
    }
    return lv[T] != 0;
}

LL Flow(int u, LL f) {
    if (u == T) return f;
    LL d = 0, s = 0;
    for (int &i = iter[u]; i; i = nxt[i]) if (w[i] && lv[to[i]] == lv[u] + 1) {
        d = Flow(to[i], std::min(f, w[i]));
        f -= d, s += d;
        w[i] -= d, w[i ^ 1] += d;
        if (!f) break;
    }
    return s;
}

inline LL Dinic() {
    LL Ans = 0;
    while (Lvl()) {
        memcpy(iter + 1, h + 1, N * sizeof(h[0]));
        Ans += Flow(S, Inf);
    }
    return Ans;
}

int main() {
    scanf("%d%d%d%d", &N, &M, &S, &T);
    for (int i = 1; i <= M; ++i) {
        int u, v; LL c;
        scanf("%d%d%lld", &u, &v, &c);
        ins(u, v, c), ins(v, u, 0ll);
    }
    printf("%lld\n", Dinic());
    return 0;
}

#104. 普通平衡树

经典的可以使用平衡树维护的问题之一。使用无旋 Treap 解决此问题。

无旋 Treap 以分裂(Split)和合并(Merge)操作为基础,使用随机化作为复杂度保证。

优点是适用范围广,代码难度低,易可持久化。缺点是常数略微大(但是很少被卡),不能作为 Link-Cut Tree 的辅助数据结构(需使用 Splay)。

#include <cstdio>

inline unsigned ran() {
    static unsigned x = 1;
    return x ^= x << 13, x ^= x >> 17, x ^= x << 5;
}

const int MN = 100005;

int Root, val[MN], pri[MN], siz[MN], ls[MN], rs[MN], cnt;
inline int NewNode(int v) { val[++cnt] = v; pri[cnt] = ran(); siz[cnt] = 1; return cnt; }
inline void Comb(int rt) { siz[rt] = siz[ls[rt]] + siz[rs[rt]] + 1; }
int Merge(int rt1, int rt2) {
    if (!rt1 || !rt2) return rt1 + rt2;
    if (pri[rt1] > pri[rt2]) {
        rs[rt1] = Merge(rs[rt1], rt2);
        Comb(rt1);
        return rt1;
    }
    else {
        ls[rt2] = Merge(rt1, ls[rt2]);
        Comb(rt2);
        return rt2;
    }
}
void Split(int rt, int k, int &rt1, int &rt2) {
    if (!rt) { rt1 = rt2 = 0; return ; }
    if (k <= siz[ls[rt]]) {
        Split(ls[rt], k, rt1, rt2);
        ls[rt] = rt2;
        Comb(rt);
        rt2 = rt;
    }
    else {
        Split(rs[rt], k - siz[ls[rt]] - 1, rt1, rt2);
        rs[rt] = rt1;
        Comb(rt);
        rt1 = rt;
    }
}
int Rank(int rt, int x) {
    if (!rt) return 0;
    if (x <= val[rt]) return Rank(ls[rt], x);
    else return siz[ls[rt]] + 1 + Rank(rs[rt], x);
}

int N;

int main() {
    scanf("%d", &N);
    for (int i = 1; i <= N; ++i) {
        int opt, x;
        scanf("%d%d", &opt, &x);
        int rt1, rt2, rt3;
        if (opt == 1) {
            Split(Root, Rank(Root, x), rt1, rt2);
            Root = Merge(rt1, Merge(NewNode(x), rt2));
        }
        if (opt == 2) {
            Split(Root, Rank(Root, x), rt1, rt2);
            Split(rt2, 1, rt2, rt3);
            Root = Merge(rt1, rt3);
        }
        if (opt == 3) {
            printf("%d\n", Rank(Root, x) + 1);
        }
        if (opt == 4) {
            Split(Root, x - 1, rt1, rt2);
            Split(rt2, 1, rt2, rt3);
            printf("%d\n", val[rt2]);
            Root = Merge(rt1, Merge(rt2, rt3));
        }
        if (opt == 5) {
            Split(Root, Rank(Root, x) - 1, rt1, rt2);
            Split(rt2, 1, rt2, rt3);
            printf("%d\n", val[rt2]);
            Root = Merge(rt1, Merge(rt2, rt3));
        }
        if (opt == 6) {
            Split(Root, Rank(Root, x + 1), rt1, rt2);
            Split(rt2, 1, rt2, rt3);
            printf("%d\n", val[rt2]);
            Root = Merge(rt1, Merge(rt2, rt3));
        }
    }
    return 0;
}

#2030. 「SDOI2016」储能表

比较有难度的二进制数位 DP。

答案可以表示为 \(\sum_{0\le i<n}\sum_{0\le j<m}(i\:\mathrm{xor}\:j)[(i\:\mathrm{xor}\:j)\ge k]-k\sum_{0\le i<n}\sum_{0\le j<m}[(i\:\mathrm{xor}\:j)\ge k]\)

使用 \(\mathrm{g}[b][x=0/1][y=0/1][z=0/1]\) 表示从高到低考虑到第 \(b\) 位,\(x=0\) 表示考虑的数 \(i\) 当前小于 \(n\)\(x=1\) 表示等于 \(n\)\(y=0\) 表示考虑的数 \(j\) 当前小于 \(m\)\(y=1\) 表示等于 \(m\)\(z=0\) 表示 \((i\:\mathrm{xor}\:j)\) 当前大于 \(k\)\(z=0\) 表示等于 \(k\) 时的 \((i\:\mathrm{xor}\:j)\) 之和。
类似可以定义 \(\mathrm{f}[b][x=0/1][y=0/1][z=0/1]\) 表示同样条件下的满足条件的个数。

则答案等于 \(\mathrm{g}[0][0][0][0]+\mathrm{g}[0][0][0][1]-k(\mathrm{f}[0][0][0][0]+\mathrm{f}[0][0][0][1])\)

转移时枚举上一位的 \(x,y,z\),以及 \(i,j\) 在这一位的值,由此计算出这一位的 \(x,y,z\) 进行转移,注意判断是否合法。

#include <cstdio>
#include <cstring>

typedef long long LL;
const int B = 59;

LL N, M, K; int P;
inline void A(int &x, int y) { x += y; if (x >= P) x -= P; }

int f[61][2][2][2], g[61][2][2][2];
LL DP() {
    memset(f, 0, sizeof f);
    memset(g, 0, sizeof g);
    f[B + 1][1][1][1] = 1;
    for (int i = B; ~i; --i) {
        int n = N >> i & 1;
        int m = M >> i & 1;
        int k = K >> i & 1;
        #define F(i) for (int i = 0; i < 2; ++i)
        F(ln) F(nn) if (!ln || n || !nn)
        F(lm) F(nm) if (!lm || m || !nm)
        F(lk) if (!lk || !k || (nn ^ nm)) {
            int nk = nn ^ nm;
            int nnn = ln & (n == nn), nnm = lm & (m == nm), nnk = lk & (k == nk);
            A(f[i][nnn][nnm][nnk], f[i + 1][ln][lm][lk]);
            A(g[i][nnn][nnm][nnk], (g[i + 1][ln][lm][lk] + ((nk ? 1ll : 0ll) << i) % P * f[i + 1][ln][lm][lk]) % P);
        }
    }
    return (g[0][0][0][0] + g[0][0][0][1] - (LL)K % P * (f[0][0][0][0] + f[0][0][0][1]) % P + P) % P;
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%lld%lld%lld%d", &N, &M, &K, &P);
        printf("%lld\n", DP());
    }
    return 0;
}

#6277. 数列分块入门 1

经典的使用分块或数据结构解决的问题之一。使用差分-前缀和技巧和树状数组可以通过此题。

#include <cstdio>

const int MN = 50005;

int N, A[MN], B[MN];

inline void Add(int i, int x) { for (; i <= N; i += i & -i) B[i] += x; }
inline int Qur(int i) { int a = 0; for (; i; i -= i & -i) a += B[i]; return a; }

int main() {
    scanf("%d", &N);
    for (int i = 1; i <= N; ++i) {
        scanf("%d", &A[i]);
        Add(i, A[i] - A[i - 1]);
    }
    for (int i = 1; i <= N; ++i) {
        int opt, l, r, c;
        scanf("%d%d%d%d", &opt, &l, &r, &c);
        if (opt == 0) Add(l, c), Add(r + 1, -c);
        if (opt == 1) printf("%d\n", Qur(r));
    }
    return 0;
}

LibreOJ 题解汇总

原文:https://www.cnblogs.com/PinkRabbit/p/LibreOJ.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!