2018冬令营模拟测试赛(十七)

2018冬令营模拟测试赛(十七)

[Problem A]Tree

试题描述

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

题解

这个数据范围肯定是树上背包了。

令 \(f(i, j, k)\) 表示子树 \(i\) 中选择了 \(j\) 个节点,路径与根的连接情况为 \(k\),具体地:

  • \(k = 0\) 时,路径的两个端点都在子树内部;
  • \(k = 1\) 时,路径的一个端点是 \(i\),另一个端点在子树内部;
  • \(k = 2\) 时,路径的两个端点都是 \(i\)。

这个树上背包的过程是:初始时只有选择根节点的情况(此时路径两端点当然都是 \(i\)),然后考虑加入一个子树,那么现在就是要讨论新路径的两个端点是否在根,若不在根,则在新加入的子树中或者原来的子树中;具体地:

  • 两个节点都不在根,那么有三种情况:

    • 两个端点都在原来的子树中;
    • 两个端点都在新加入的子树中;
    • 一个端点在新加入的子树中,另一个在原来子树中。
  • 一个节点在根,那么有两种情况:
    • 另一个节点在原来子树中;
    • 另一个节点在新加入的子树中。
  • 两个节点都在根。

转移的时候把路径的首尾接好,设新加入的子树是 \(j\),算好 \(i \rightarrow j\) 这条边经过一次还是两次。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 3010
#define maxm 6010
#define oo 700000000

int n, m, K, head[maxn], nxt[maxm], to[maxm], dist[maxm];

void AddEdge(int a, int b, int c) {
    to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
    swap(a, b);
    to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
    return ;
}

void upd(int& a, int b) {
    if(a > b) a = b;
    return ;
}
int f[maxn][maxn][2][2], siz[maxn];
void dp(int u, int fa) {
    f[u][1][1][1] = 0;
    siz[u] = 1;
    for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
        dp(to[e], u);
        dwn(i, siz[u], 1) dwn(j, siz[to[e]], 1) {
            upd(f[u][i+j][0][0], f[u][i][0][0] + f[to[e]][j][1][1] + (dist[e] << 1));
            upd(f[u][i+j][0][0], min(f[to[e]][j][0][0], min(f[to[e]][j][0][1], f[to[e]][j][1][1])) + f[u][i][1][1] + (dist[e] << 1));
            upd(f[u][i+j][0][0], f[u][i][0][1] + min(f[to[e]][j][0][1], f[to[e]][j][1][1]) + dist[e]);
            upd(f[u][i+j][0][1], f[u][i][0][1] + f[to[e]][j][1][1] + (dist[e] << 1));
            upd(f[u][i+j][0][1], f[u][i][1][1] + min(f[to[e]][j][0][1], f[to[e]][j][1][1]) + dist[e]);
            upd(f[u][i+j][1][1], f[u][i][1][1] + f[to[e]][j][1][1] + (dist[e] << 1));
        }
        siz[u] += siz[to[e]];
    }
    // rep(i, 0, siz[u]) printf("[%d][%d]: %d %d %d\n", u, i, f[u][i][0][0], f[u][i][0][1], f[u][i][1][1]);
    return ;
}

int main() {
    n = read(); K = read();
    rep(i, 1, n - 1) {
        int a = read(), b = read(), c = read();
        AddEdge(a, b, c);
    }

    rep(i, 1, n) rep(j, 0, n) rep(s, 0, 1) rep(t, 0, 1) f[i][j][s][t] = oo;
    dp(1, 0);

    int ans = oo;
    rep(i, 1, n) upd(ans, min(f[i][K][0][0], min(f[i][K][0][1], f[i][K][1][1])));
    printf("%d\n", ans);

    return 0;
}

[Problem B]Tower

试题描述

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

题解

这个题直接上哈希……

由于在首尾添加一个字符,答案只可能加 \(1\) 或加 \(2\),于是分别维护一下首尾正反长度为 \(nowans\) 和 \(nowans + 1\) 的串的哈希值就好了,有必要再开一个串记录当前串长什么样。然后记得把所有东西可持久化一下(包括当前串的首尾指针、答案、\(8\) 种哈希值),其实就是用个栈维护一下,到撤销的时候直接把栈的顶指针减去它撤回的步数即可。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
    return x * f;
}

#define maxn 10000010
#define UI unsigned int
#define LL long long
#define rate 233

UI rinv;
void gcd(LL a, LL b, LL& x, LL& y) {
    if(!b){ x = 1; y = 0; return ; }
    gcd(b, a % b, y, x); y -= (a / b) * x;
    return ;
}
void getinv() {
    LL x, y;
    gcd(rate, 4294967296ll, x, y);
    rinv = x;
    return ;
}

short S[maxn<<1];
int rpow[maxn], fr[2][2][maxn], ed[2][2][maxn]; // [rev][add]
int hd[maxn], tl[maxn], ans[maxn], top;

void getMore() {
    if(ans[top] + 1 <= tl[top] - hd[top] + 1) {
        ed[0][1][top] = ed[0][0][top] + rpow[ans[top]] * S[tl[top]-ans[top]];
        ed[1][1][top] = ed[1][0][top] * rate + S[tl[top]-ans[top]];
        fr[0][1][top] = fr[0][0][top] * rate + S[hd[top]+ans[top]];
        fr[1][1][top] = fr[1][0][top] + rpow[ans[top]] * S[hd[top]+ans[top]];
    }
    else rep(rev, 0, 1) ed[rev][1][top] = fr[rev][1][top] = 0;
    return ;
}

void addr(short c) {
    top++;
    S[tl[top] = tl[top-1]+1] = c;
    hd[top] = hd[top-1];
    if(ans[top-1] + 1 <= tl[top-1] - hd[top-1] + 1 && ed[0][1][top-1] * rate + c == ed[1][1][top-1] + rpow[ans[top-1]+1] * c) { // ans + 2
        ans[top] = ans[top-1] + 2;
        ed[0][0][top] = ed[0][1][top-1] * rate + c;
        ed[1][0][top] = ed[1][1][top-1] + rpow[ans[top-1]+1] * c;
        fr[0][0][top] = fr[0][1][top-1] * rate + S[hd[top-1]+ans[top-1]+1];
        fr[1][0][top] = fr[1][1][top-1] + rpow[ans[top-1]+1] * S[hd[top-1]+ans[top-1]+1];
    }
    else if(ed[0][0][top-1] * rate + c == ed[1][0][top-1] + rpow[ans[top-1]] * c) { // ans + 1
        ans[top] = ans[top-1] + 1;
        ed[0][0][top] = ed[0][0][top-1] * rate + c;
        ed[1][0][top] = ed[1][0][top-1] + rpow[ans[top-1]] * c;
        fr[0][0][top] = fr[0][0][top-1] * rate + S[hd[top]+ans[top-1]];
        fr[1][0][top] = fr[1][0][top-1] + rpow[ans[top-1]] * S[hd[top]+ans[top-1]];
    }
    else {
        ans[top] = ans[top-1];
        ed[0][0][top] = ed[0][0][top-1] * rate + c - rpow[ans[top]] * S[tl[top]-ans[top]];
        ed[1][0][top] = (ed[1][0][top-1] + rpow[ans[top]] * c - S[tl[top]-ans[top]]) * rinv;
        fr[0][0][top] = fr[0][0][top-1];
        fr[1][0][top] = fr[1][0][top-1];
    }
    getMore();
    return ;
}

void addl(short c) {
    top++;
    S[hd[top] = hd[top-1]-1] = c;
    tl[top] = tl[top-1];
    if(ans[top-1] + 1 <= tl[top-1] - hd[top-1] + 1 && fr[0][1][top-1] + rpow[ans[top-1]+1] * c == fr[1][1][top-1] * rate + c) { // ans + 2
        ans[top] = ans[top-1] + 2;
        fr[0][0][top] = fr[0][1][top-1] + rpow[ans[top-1]+1] * c;
        fr[1][0][top] = fr[1][1][top-1] * rate + c;
        ed[0][0][top] = ed[0][1][top-1] + rpow[ans[top-1]+1] * S[tl[top-1]-ans[top-1]-1];
        ed[1][0][top] = ed[1][1][top-1] * rate + S[tl[top-1]-ans[top-1]-1];
    }
    else if(fr[0][0][top-1] + rpow[ans[top-1]] * c == fr[1][0][top-1] * rate + c) { // ans + 1
        ans[top] = ans[top-1] + 1;
        fr[0][0][top] = fr[0][0][top-1] + rpow[ans[top-1]] * c;
        fr[1][0][top] = fr[1][0][top-1] * rate + c;
        ed[0][0][top] = ed[0][0][top-1] + rpow[ans[top-1]] * S[tl[top]-ans[top-1]];
        ed[1][0][top] = ed[1][0][top-1] * rate + S[tl[top]-ans[top-1]];
    }
    else {
        ans[top] = ans[top-1];
        fr[0][0][top] = (fr[0][0][top-1] + rpow[ans[top]] * c - S[hd[top]+ans[top]]) * rinv;
        fr[1][0][top] = fr[1][0][top-1] * rate + c - rpow[ans[top]] * S[hd[top]+ans[top]];
        ed[0][0][top] = ed[0][0][top-1];
        ed[1][0][top] = ed[1][0][top-1];
    }
    getMore();
    return ;
}

int main() {
    getinv();
    top = 1;
    hd[1] = maxn; tl[1] = maxn - 1; ans[1] = 0;
    rep(rev, 0, 1) rep(add, 0, 1) fr[rev][add][1] = ed[rev][add][1] = 0;

    int q = read(), lst = 0; char c = Getchar(); LL sans = 0;
    rpow[0] = 1; rep(i, 1, q + 1) rpow[i] = rpow[i-1] * rate;
    while(!isdigit(c)) c = Getchar();
    rep(i, 1, q) {
        short op = c - ‘0‘, x = 0;
        c = Getchar(); x = c - ‘0‘; c = Getchar(); x = x * 10 + c - ‘0‘; if(i < q) c = Getchar();
        x = (x + lst) % 100;
        if(op == 1) addr(x);
        if(op == 2) addl(x);
        if(op == 3) top -= x;
        lst = ans[top];
        // printf("%d %d: %d\n", op, x, lst);
        sans += lst;
    }

    printf("%lld\n", sans);

    return 0;
}

[Problem C]Alice and Bob VII

试题描述

从前,有 \(n\) 个盒子(编号从 \(1\) 到 \(n\)),\(m\) 把钥匙(编号从 \(1\) 到 \(m\))和 \(d\) 个商店(编号从 \(1\) 到 \(d\))。编号为 \(i\) 的钥匙可以打开编号为 \(a_{i,1},a_{i,2},\cdots,a_{i,k_i}\) 的盒子。但是,一把钥匙一旦打开了一个盒子,它会立刻消失。因此,一把钥匙不能打开多个盒子。编号为 \(i\) 的钥匙只在商店 \(s_i\) 出售,价格为 \(c_i\)

个金币。Alice 不能多次购买同一把钥匙。现在,Alice 想买一些钥匙来打开所有的盒子。

Bob 想来捣乱。他可以在 Alice 决定购买钥匙之前改变某些钥匙的价格。如果 Bob 支付 \(b_j\) 个金币,Bob 就可以将商店 \(j\) 中所有钥匙的价格全部提高 \(1\) 金币。对于每家商店,他可以重复上述过程任意非负整数次。例如:如果他支付 \(2b_j\) 个金币,他可以将商店 \(j\) 中所有钥匙的价格全部提高 \(2\) 金币;但是,他不能支付 \(\frac{b_j}{2}\) 个金币来将价格提高 \(0.5\) 金币。

Alice 的目的是最小化 \((Alice支付的金币 - Bob支付的金币)\),Bob 的目的是最大化该值。假设 Alice 和 Bob 都以最优策略来操作,请你计算最终该值是多少。

如果该值为无穷大,输出 \(-1\)。

数据保证如果 Bob 不来捣乱,Alice 是可以打开所有盒子的。

输入

第一行三个整数 \(n,m,d(1 \le n \le 100,1 \le m \le 1000,1 \le n,d \le m)\)

接下来 \(m\) 行,每行表示一把钥匙。每行开头有三个整数 \(c_i,s_i,k_i\) ,分别表示钥匙价格,钥匙出售商店的编号,这把钥匙能打开多少个盒子。接下来 \(k_i\) 个整数,表示这把钥匙能打开的盒子的编号。\((1 \le c_i \le 1000,1 \le s_i \le d,1 \le k_i \le \mathrm{min}(10,n),1 \le a_i,j \le n, and\ if\ j \ne k,a_{i,j} \ne a_{i,k})\)

接下来 \(d\) 行,每行一个整数 \(b_i(1 \le b_i \le 1000)\)。

输出

输出一个整数表示答案。

输入示例1

3 4 1
2 1 2 1 2
2 1 2 2 3
2 1 2 3 1
3 1 3 1 2 3
5

输出示例1

6

输入示例2

3 4 1
2 1 2 1 2
2 1 2 2 3
2 1 2 3 1
3 1 3 1 2 3
2

输出示例2

-1

输入示例2

2 3 2
3 1 2 1 2
4 1 1 2
5 2 2 1 2
1
2

输出示例2

8

数据规模及约定

对于 \(30\%\) 的数据,\(1 \le n \le 5, 1 \le m \le 10\)

对于所有数据,\(1 \le n \le 100, 1 \le m \le 1000, 1 \le n,d \le m\)

题解

这是一道处于未知领域的题……要用到拉格朗日乘数法证明它的正确性,但是仔细想一想发现还是证明不了,把原题解丢在这里吧,作为一个未解问题。(或许这题彻头彻尾地错了呢?)

拉格朗日乘数法和KKT条件

支持向量机:Duality

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 2110
#define maxm 24210
#define oo 2147483647

struct Edge {
    int from, to, flow, cost;
    Edge() {}
    Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), flow(_3), cost(_4) {}
} es[maxm];
struct ZKW {
    int n, m, s, t, cost, ans, head[maxn], nxt[maxm];
    Edge es[maxm];
    int d[maxn], Q[maxn], hd, tl;
    bool inq[maxn];
    bool vis[maxn];

    void init() {
        m = 0; memset(head, -1, sizeof(head));
        return ;
    }
    void setn(int _) {
        n = _;
        return ;
    }

    void AddEdge(int a, int b, int c, int d) {
        es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++;
        es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++;
        return ;
    }

    int Nxt(int x) { return (x + 1) % maxn; }
    bool BFS() {
        rep(i, 1, n) d[i] = oo;
        d[t] = 0;
        hd = tl = 0; Q[tl = Nxt(tl)] = t; inq[t] = 1;
        while(hd != tl) {
            int u = Q[hd = Nxt(hd)]; inq[u] = 0;
            for(int i = head[u]; i != -1; i = nxt[i]) {
                Edge& e = es[i^1];
                if(e.flow && d[e.from] > d[u] + e.cost) {
                    d[e.from] = d[u] + e.cost;
                    if(!inq[e.from]) inq[e.from] = 1, Q[tl = Nxt(tl)] = e.from;
                }
            }
        }
        if(d[s] == oo) return 0;
        cost = d[s];
        return 1;
    }

    int DFS(int u, int a) {
        if(u == t || !a) return ans += cost * a, a;
        if(vis[u]) return 0;
        vis[u] = 1;
        int flow = 0, f;
        for(int i = head[u]; i != -1; i = nxt[i]) {
            Edge& e = es[i];
            if(d[e.to] == d[u] - e.cost && (f = DFS(e.to, min(a, e.flow)))) {
                flow += f; a -= f;
                e.flow -= f; es[i^1].flow += f;
                if(!a) return flow;
            }
        }
        return flow;
    }

    int MaxFlow(int _s, int _t) {
        s = _s; t = _t;
        int flow = 0, f;
        while(BFS())
            do {
                memset(vis, 0, sizeof(vis));
                f = DFS(s, oo);
                flow += f;
            } while(f);
        return flow;
    }
} sol;

int CntP;
struct Point {
    int id;
    Point(): id(0) {}
    int p() { return id ? id : id = ++CntP; }
} box[maxn], key[maxn], shop[maxn], S, T;
vector <int> idm[maxn];
int addcost[maxn];

int main() {
    int bx = read(), ky = read(), shp = read();
    sol.init();
    rep(i, 1, ky) {
        int pri = read(), sid = read(), k = read();
        idm[sid].push_back(sol.m);
        sol.AddEdge(shop[sid].p(), key[i].p(), 1, pri);
        rep(j, 1, k) sol.AddEdge(key[i].p(), box[read()].p(), 1, 0);
    }
    rep(i, 1, shp) sol.AddEdge(S.p(), shop[i].p(), addcost[i] = read(), 0);
    rep(i, 1, bx) sol.AddEdge(box[i].p(), T.p(), 1, 0);
    sol.setn(CntP);

    if(sol.MaxFlow(S.p(), T.p()) < bx) return puts("-1"), 0;
    printf("%d\n", sol.ans);

    return 0;
}

原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8313306.html

时间: 2024-10-11 01:09:49

2018冬令营模拟测试赛(十七)的相关文章

2018冬令营模拟测试赛(三)

2018冬令营模拟测试赛(三) [Problem A]摧毁图状树 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见"试题描述" 输出示例 见"试题描述" 数据规模及约定 见"试题描述" 题解 这题没想到贪心 QwQ,那就没戏了-- 贪心就是每次选择一个最深的且没有被覆盖的点向上覆盖 \(k\) 层,因为这个"最深的没有被覆盖的点"不可能再有其它点引出的链覆盖它了,而它又

2018冬令营模拟测试赛(五)

2018冬令营模拟测试赛(五) [Problem A][UOJ#154]列队 试题描述 picks 博士通过实验成功地得到了排列 \(A\),并根据这个回到了正确的过去.他在金星凌日之前顺利地与丘比签订了契约,成为了一名马猴烧酒. picks 博士可以使用魔法召唤很多很多的猴子与他一起战斗,但是当猴子的数目 \(n\) 太大的时候,训练猴子就变成了一个繁重的任务. 历经千辛万苦,猴子们终于学会了按照顺序排成一排.为了进一步训练,picks 博士打算设定一系列的指令,每一条指令 \(i\) 的效果

2018冬令营模拟测试赛(十九)

2018冬令营模拟测试赛(十九) [Problem A]小Y 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见"试题描述" 输出示例 见"试题描述" 数据规模及约定 见"试题描述" 题解 目前未知. 这题目前就能做到 \(O(n \sqrt{M} \log n)\),其中 \(M\) 是逆序对数,然而会被卡 \(T\):当然这题暴力可以拿到和左边那个算法一样的分数,只要暴力加一个剪枝:当左

2018.8.6 Noip2018模拟测试赛(十九)

日期: 八月六号  总分: 300分  难度: 提高 ~ 省选    得分: 10分(MMP) 题目目录: T1:Tree T2:异或运算 T3:Tree Restoring 赛后反思: Emmmmmmm…… 一直在打第一题…… 结果考完才发现dp少了一种情况…… 除此之外,我无话可说…… Emmmmmm…… 题解: T1:Tree 树形背包dp,设$f[i][j][k(0/1/2)]$为$i$的子树中,选$j$条边,0:从$i$出发,到$i$结束/1:从$i$出发,到$i$的某个后代结束/2:

【20180318】2018北京集训测试赛(六)

菜鸡滚回石家庄了233 Problem B: 求和 题解&反思: 好久没写反演了真刺激 大力推公式就好咯 \[ \sum_{i=1}^{n}\sum_{j=1}^{i}\sum_{k=1}^{i}gcd(i,j,k) \] \[ =\sum_{i=1}^{n}\sum_{d|i}d\sum_{j=1}^{i}\sum_{k=1}^{i}[gcd(i,j,k)==d] \] \[ =\sum_{i=1}^{n}\sum_{d|i}d\sum_{j=1}^{\left \lfloor \frac{i

[CSP-S模拟测试]:赛(贪心+三分)

题目描述 由于出题人思维枯竭所以想不出好玩的背景.有$n$个物品,第$i$个物品的价格是$v_i$,有两个人,每个人都喜欢$n$个物品中的一些物品.要求选出正好$m$个物品,满足选出的物品中至少有$k$个物品被第一个人喜欢,$k$个物品被第二个人喜欢.并求出最小的价格和. 输入格式 第一行三个数$n,m,k$.第二行$n$个数,第$i$个数表示$v_i$.第三行包含一个数$a$,表示第一个人喜欢的物品数.第四行包含$a$个数,表示第一个人喜欢的物品是哪几个.第五行包含一个数$b$,表示第二个人喜

2018 蓝桥杯省赛 B 组模拟赛(一)

2018 蓝桥杯省赛 B 组模拟赛(一) A.今天蒜头君带着花椰妹和朋友们一起聚会,当朋友们问起年龄的时候,蒜头君打了一个哑谜(毕竟年龄是女孩子的隐私)说:“我的年龄是花椰妹年龄个位数和十位数之和的二倍”. 花椰妹看大家一脸懵逼,就知道大家也不知道蒜头君的年龄,便连忙补充道:“我的年龄是蒜头君个位数和十位数之和的三倍”. 请你计算:蒜头君和花椰妹年龄一共有多少种可能情况? 提醒:两位的年龄都是在 [10,100)[10,100) 这个区间内. 题解: 暴力枚举 answer: 1 代码如下: #

2018.10.2浪在ACM 集训队第二次测试赛

2018.10.26 浪在ACM 集训队第二次测试赛 题目一览表(Green color indicate understand and Accept) 来源 考察知识点 完成时间 A 1273 海港(比赛过题) NOIP 普及组 2016 查分数组+二分 2018.10.26 D 1272 回问日期(比赛过题) NOIP 普及组 2016 思维? 2018.10.26 G 1268 扫雷游戏(比赛过题) NOIP 普及组 2015 暴力 or DFS 2018.10.26          

2018.11.16 浪在ACM 集训队第五次测试赛

2018.11.16 浪在ACM 集训队第五次测试赛 整理人:李继朋 Problem A : 参考博客:[1]朱远迪 Problem B : 参考博客: Problem C : 参考博客: Problem D : 参考博客:[1]朱远迪 Problem E : 参考博客: 原文地址:https://www.cnblogs.com/QLU-ACM/p/9977949.html