• ICPC第一场网络赛 题解 + 补题


    一些闲话旅行传送门

    A. Busiest Computing Nodes

    题意:给你一个含有 \(k\) 个节点,编号从 \(0\)\(k-1\) 的计算集群,以及一组已知到达时间处理时间的请求(亦从 \(0\) 开始编号)。

    当每个请求到达时,它会优先进入第 (\(i\) % \(k\)) 个节点,若当前节点正忙,则根据开放定址法去找下一个空闲节点(如果最终都没能找到空闲的节点,该请求将被忽略)。

    现在问你在发送完这组请求后,哪些节点处理的请求数量最多?

    题目分析:显然,请求的结束时间(即节点可以被重新启用的时间)= 到达时间 + 处理时间。

    先考虑暴力的做法,新请求到来时扫一遍当前节点,如果有节点满足条件(节点内请求的结束时间 \(\leq\) 当前请求的到达时间)就进行更新,时间复杂度约为 \(O(nk)\) ,必 \(T\)

    这里采取线段树+二分查询优化,时间复杂度 \(O(nlogk)\) ,具体看图:
    A1.png

    A2.png

    • 线段树维护区间最小值,单点修改,区间查询
    • 根据最小值进行二分,为了方便查询我拷贝了一份节点(也可以先查 \(i\) ~ \(k-1\) ,再查 \(0\) ~ \(i-1\) ),更新时同时更新两个就好。
    • 注意输出格式,行尾无空格(白 PE 六发,真的傻逼)

    AC代码

    #include <bits/stdc++.h>
    using ll = long long;
    using namespace std;
    const int maxn = 2e5 + 10;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline ll read()
    {
        ll x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    ll k, n, ans[maxn];
    
    struct obj
    {
        ll idx, ar, pro;
    } a[maxn];
    
    #define lson k << 1
    #define rson k << 1 | 1
    struct node
    {
        int s, t;
        ll mn;
    } tree[maxn << 2];
    
    void pushup(int k) { tree[k].mn = min(tree[lson].mn, tree[rson].mn); }
    
    void build(int k, int s, int t)
    {
        tree[k].s = s, tree[k].t = t;
        if (s == t)
            return;
        int mid = (s + t) >> 1;
        build(lson, s, mid);
        build(rson, mid + 1, t);
        pushup(k);
    }
    
    void update(int k, int p, ll x)
    {
        if (tree[k].s == tree[k].t && tree[k].s == p)
        {
            tree[k].mn = x;
            return;
        }
        int mid = (tree[k].s + tree[k].t) >> 1;
        p <= mid ? update(lson, p, x) : update(rson, p, x);
        pushup(k);
    }
    
    ll query(int k, int s, int t)
    {
        if (s <= tree[k].s && tree[k].t <= t)
            return tree[k].mn;
        int mid = (tree[k].s + tree[k].t) >> 1;
        if (t <= mid)
            return query(lson, s, t);
        else if (s > mid)
            return query(rson, s, t);
        else
            return min(query(lson, s, mid), query(rson, mid + 1, t));
    }
    
    int main()
    {
        k = read(), n = read();
        for (int i = 1; i <= n; i++)
            a[i].ar = read(), a[i].pro = read(), a[i].idx = i - 1;
        build(1, 1, 2 * k);
        for (int i = 1; i <= n; i++)
        {
            ll st = a[i].idx % k + 1, ed = st + k - 1;
            int l = st, r = ed;
            while (l < r)
            {
                int mid = (l + r) >> 1;
                if (query(1, l, mid) <= a[i].ar)
                    r = mid;
                else
                    l = mid + 1;
            }
            ll dest = l;
            if (query(1, dest, dest) <= a[i].ar)
            {
                update(1, dest, a[i].ar + a[i].pro);
                if (dest <= k)
                    update(1, dest + k, a[i].ar + a[i].pro), ans[dest - 1]++;
                else
                    update(1, dest - k, a[i].ar + a[i].pro), ans[dest - k - 1]++;
            }
        }
        ll mx = 0;
        for (int i = 0; i < k; i++)
            mx = max(mx, ans[i]);
        vector<int> realans;
        for (int i = 0; i < k; i++)
            if (ans[i] == mx)
                realans.push_back(i);
        for (int i = 0; i < realans.size() - 1; i++)
            printf("%d ", realans[i]);
        printf("%d", realans[realans.size() - 1]);
        return 0;
    }
    

    D. Edge of Taixuan

    题意:给你 \(n\) 个点和 \(m\) 次操作,每次操作令区间 \([l, r]\) 中的点两两相连构成一张边权为 \(w\) 的完全图,求要得到最小生成树所需删除边的边权总和。

    题目分析:将所有操作按边权降序排序,接下来按区间覆盖问题来做就好,最终得到的最小生成树一定是一条链,答案即为总边权减去最小生成树的权重,线段树与分块均可,分块要跑得快一些。

    AC代码(线段树)

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    using ll = long long;
    const ll inf = 1e10;
    const int maxn = 5e5 + 5;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    struct cable
    {
        int l, r, w;
        bool operator<(const cable &lhs) const { return lhs.w < w; }
    } q[maxn];
    
    #define lson k << 1
    #define rson k << 1 | 1
    struct node
    {
        int l, r, flag;
        ll sum, tag;
    } tree[maxn << 2];
    
    void pushup(int k)
    {
        if (tree[lson].flag && tree[rson].flag)
            tree[k].flag = 1;
        tree[k].sum = tree[lson].sum + tree[rson].sum;
    }
    
    void pushdown(int k)
    {
        ll v = tree[k].tag;
        tree[lson].sum = (tree[lson].r - tree[lson].l + 1) * v;
        tree[rson].sum = (tree[rson].r - tree[rson].l + 1) * v;
        tree[lson].tag = tree[rson].tag = v;
        tree[k].tag = 0;
    }
    
    void build(int k, int l, int r)
    {
        tree[k].l = l, tree[k].r = r;
        tree[k].sum = inf, tree[k].tag = tree[k].flag = 0;
        if (l == r)
            return;
        int mid = (l + r) >> 1;
        build(lson, l, mid);
        build(rson, mid + 1, r);
    }
    
    void update(int k, int l, int r, int x)
    {
        if (l <= tree[k].l && tree[k].r <= r)
        {
            tree[k].sum = (tree[k].r - tree[k].l + 1) * x;
            tree[k].tag = x;
            tree[k].flag = 1;
            return;
        }
        if (tree[k].tag)
            pushdown(k);
        int mid = (tree[k].l + tree[k].r) >> 1;
        if (r <= mid)
            update(lson, l, r, x);
        else if (l > mid)
            update(rson, l, r, x);
        else
            update(lson, l, mid, x), update(rson, mid + 1, r, x);
        pushup(k);
    }
    
    void solve()
    {
        ll ans = 0;
        int n = read() - 1, m = read();
        build(1, 1, n);
        rep(i, 1, m)
        {
            q[i].l = read(), q[i].r = read(), q[i].w = read();
            ans += 1ll * (q[i].r - q[i].l + 1) * (q[i].r - q[i].l) / 2 * q[i].w;
        }
        std::sort(q + 1, q + m + 1);
        rep(i, 1, m) update(1, q[i].l, q[i].r - 1, q[i].w);
        if (!tree[1].flag)
            puts("Gotta prepare a lesson");
        else
            printf("%lld\n", ans - tree[1].sum);
    }
    
    int main(int argc, char const *argv[])
    {
        int T = read();
        rep(cas, 1, T)
        {
            printf("Case #%d: ", cas);
            solve();
        }
        return 0;
    }
    
    

    AC代码(分块)

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    using ll = long long;
    const ll inf = 1e10;
    const int maxn = 5e5 + 5;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    int n, m, block, num, l[maxn], r[maxn], bel[maxn];
    ll tag[maxn], a[maxn];
    
    struct cable
    {
        int l, r, w;
        bool operator<(const cable &rhs) const { return w > rhs.w; }
    } q[maxn];
    
    void build()
    {
        rep(i, 1, n) a[i] = tag[i] = inf;
        block = sqrt(n), num = (n - 1) / block + 1;
        rep(i, 1, n) bel[i] = (i - 1) / block + 1;
        rep(i, 1, num) l[i] = (i - 1) * block + 1, r[i] = i * block;
        r[num] = n;
    }
    
    void update(int ql, int qr, int c)
    {
        int bl = bel[ql], br = bel[qr];
        rep(i, ql, std::min(qr, r[bl])) a[i] = c;
        if (bl ^ br)
        {
            rep(i, bl + 1, br - 1) tag[i] = c;
            rep(i, l[br], qr) a[i] = c;
        }
    }
    
    void solve()
    {
        ll ans = 0, sum = 0;
        n = read() - 1, m = read();
        build();
        rep(i, 1, m)
        {
            q[i].l = read(), q[i].r = read(), q[i].w = read();
            ans += 1ll * (q[i].r - q[i].l + 1) * (q[i].r - q[i].l) / 2 * q[i].w;
        }
        std::sort(q + 1, q + m + 1);
        rep(i, 1, m) update(q[i].l, q[i].r - 1, q[i].w);
        rep(i, 1, n) sum += std::min(a[i], tag[bel[i]]);
        if (sum >= inf)
            puts("Gotta prepare a lesson");
        else
            printf("%lld\n", ans - sum);
    }
    
    int main(int argc, char const *argv[])
    {
        int T = read();
        rep(cas, 1, T)
        {
            printf("Case #%d: ", cas);
            solve();
        }
        return 0;
    }
    

    F. Land Overseer

    题意:给你两个圆心分别为 \((a,b)\)\((2a,0)\) 的圆 \(A\)\(B\) ,半径均为 \(r\) 的圆,现从原点出发,问先经过圆 \(A\) 再经过圆 \(B\) 的最短路径是多少?

    题目分析:分两种情况讨论,具体看图和代码:
    F.png

    AC代码

    #include <bits/stdc++.h>
    using ll = long long;
    using namespace std;
    
    int T;
    ll a, b, R;
    
    int main()
    {
        scanf("%d", &T);
        for (int i = 1; i <= T; i++)
        {
            scanf("%lld%lld%lld", &a, &b, &R);
            double ans = 0;
            if (a > R && b <= R)
                ans = 2.0 * a - 1.0 * R;
            else
                ans = 2.0 * sqrt(a * a + (b - R) * (b - R)) - 1.0 * R;
            printf("Case #%d: %.2lf\n", i, ans);
        }
        return 0;
    }
    

    H. Mesh Analysis

    题意:给你 \(n\) 个点的坐标,这些点构成了一些三角形和线段。每次询问一个点所有的邻点与所在图形的编号。

    题目分析:最开始 \(cx\) 理解的意思就是对的,我也想过坐标是不是没用,但可惜没能猜透出题人的心思,往复杂的方面去想了,赛后交流的时候发现有几支队伍也想歪了,大家都在考虑怎么判断某个点包不包含在其它三角形内,一致认为这是个几何题,各种叉乘去搞。

    结果笑死,哪有我们想的这么高大上,用 \(map\) 直接记录就完事了。md搁着猜谜呢,和出题人心意不相通还写不了题了。

    AC代码

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    #define IOS                      \
        ios::sync_with_stdio(false); \
        cin.tie(nullptr);            \
        cout.tie(nullptr)
    using db = double;
    using namespace std;
    
    map<int, set<int>> mp1;
    map<int, set<int>> mp2;
    
    int main(int argc, char const *argv[])
    {
        IOS;
        int n, m;
        cin >> n >> m;
        int id, type;
        db x, y, z;
        rep(i, 1, n) cin >> id >> x >> y >> z;
        rep(i, 1, m)
        {
            cin >> id >> type;
            if (type == 203)
            {
                cin >> x >> y >> z;
                mp1[x].insert(y), mp1[x].insert(z);
                mp1[y].insert(x), mp1[y].insert(z);
                mp1[z].insert(x), mp1[z].insert(y);
                mp2[x].insert(id), mp2[y].insert(id), mp2[z].insert(id);
            }
            else
            {
                cin >> x >> y;
                mp1[x].insert(y), mp1[y].insert(x);
                mp2[x].insert(id), mp2[y].insert(id);
            }
        }
        int q;
        cin >> q;
        rep(i, 1, q)
        {
            cin >> id;
            cout << id << endl;
            cout << "[";
            for (auto it : mp1[id])
            {
                if (it ^ *mp1[id].begin())
                    cout << ",";
                cout << it;
            }
            cout << "]" << endl;
            cout << "[";
            for (auto it : mp2[id])
            {
                if (it ^ *mp2[id].begin())
                    cout << ",";
                cout << it;
            }
            cout << "]";
            if (i ^ q)
                cout << "" << endl;
        }
        return 0;
    }
    

    题意:给你一个序列 \(S\) 和一个数 \(A\) ,找出序列中所有与 \(A\) 的差值 \(\leq r\) 的元素,降序输出。

    题目分析:签到题,降序排列后 \(O(n)\) 扫一遍就好,难点主要在处理输入数据上。

    AC代码

    #include <bits/stdc++.h>
    using ll = long long;
    using namespace std;
    const int maxn = 1e5 + 10;
    
    ll s[maxn], len, ans;
    
    int main()
    {
        while (scanf("%lld", &s[++len]) != EOF)
            ;
        len -= 3;
        sort(s + 1, s + len + 1, greater<ll>());
        ll a = s[len + 1], r = s[len + 2];
        for (int i = 1; i <= len; i++)
            if (abs(s[i] - a) <= r)
                printf("%lld ", s[i]), ++ans;
        if (!ans)
            printf("\n");
        return 0;
    }
    

    J. Red-Black Paths

    题意:给你一张初始全为白点的空图,按照时间顺序建图,建图过程中会将白点染成红黑色,求相邻两次询问间新增的红黑路(红点到黑点)的路径长的异或和,红黑路的路径长指的是路径上每个点的编号乘以当前长度的总和。

    题目分析:离线算法,记录每个操作当前的时间戳。先 \(dfs\) 标记所有能到达黑点的点,然后构造新图,只把有用的边连上,再对每个红点进行 \(dfs\) 计算其构成的每条红黑路的路径长,根据之前记录的时间戳来更新答案以保证正确性。最后维护个前缀异或和,对每个询问输出 \(ans_{q[i]} \bigoplus ans_{q[i-1]}\) 即可。

    AC代码

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    #define pii std::pair<int, int>
    #define mp std::make_pair
    const int maxn = 2e5 + 5;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    int n, m, col[maxn], T[maxn], vis[maxn], tag[maxn], ans[maxn];
    struct edge
    {
        int u, v, t;
    };
    std::vector<int> red, q;
    std::vector<pii> g[maxn];
    std::vector<edge> e;
    
    void dfs1(int u)
    {
        vis[u] = 1;
        if (col[u] == 2)
            tag[u] = 1;
        for (auto x : g[u])
        {
            int v = x.first;
            if (!vis[v])
                dfs1(v);
            tag[u] |= tag[v];
        }
    }
    
    void dfs2(int u, int len, int sum, int t)
    {
        sum += u * len++;
        if (col[u] == 2)
            ans[std::max(T[u], t)] ^= sum;
        for (auto x : g[u])
            dfs2(x.first, len, sum, std::max(x.second, t));
    }
    
    int main(int argc, char const *argv[])
    {
        m = read();
        int op, u, v;
        q.push_back(0);
        rep(i, 1, m)
        {
            op = read();
            switch (op)
            {
            case 1:
                u = read(), v = read(), n = std::max({n, u, v});
                g[u].push_back(mp(v, i)), e.push_back(edge{u, v, i});
                break;
            case 2:
                u = read(), red.push_back(u);
                col[u] = 1, T[u] = i;
                break;
            case 3:
                u = read();
                col[u] = 2, T[u] = i;
                break;
            default:
                q.push_back(i);
                break;
            }
        }
        rep(i, 1, n) if (!vis[i]) dfs1(i);
        rep(i, 1, n) g[i].clear();
        for (auto x : e)
            if (tag[x.u] && tag[x.v])
                g[x.u].push_back(mp(x.v, x.t));
        for (auto x : red)
            dfs2(x, 1, 0, T[x]);
        rep(i, 1, m) ans[i] ^= ans[i - 1];
        rep(i, 1, q.size() - 1) printf("%d\n", ans[q[i]] ^ ans[q[i - 1]]);
        return 0;
    }
    

    K. Segment Routing

    题意:模拟题,给你一张有向图,每次询问从节点 \(i\) 开始按指定方向走最终到达的点。

    题目分析:阅读理解+模拟,跟着题意来就好,越界就判定丢包。

    AC代码

    #include <bits/stdc++.h>
    using ll = long long;
    const int maxn = 1e5 + 5;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    std::vector<int> e[maxn];
    
    int main()
    {
        int T = read();
        for (int cas = 1; cas <= T; cas++)
        {
            printf("Case #%d: \n", cas);
            int n = read(), m = read();
            for (int i = 1; i <= n; i++)
                e[i].clear();
            for (int i = 1; i <= n; i++)
            {
                int d = read();
                for (int j = 1; j <= d; j++)
                {
                    int v = read();
                    e[i].push_back(v);
                }
            }
            for (int i = 1; i <= m; i++)
            {
                int u = read(), l = read();
                int flag = 1;
                for (int j = 1; j <= l; j++)
                {
                    int v = read();
                    if (!flag)
                        continue;
                    if (e[u].size() < v)
                    {
                        puts("Packet Loss");
                        flag = 0;
                        continue;
                    }
                    u = e[u][v - 1];
                }
                if (flag)
                    printf("%d\n", u);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    国货之光业务增长背后的技术支持
    减少运维工作量,如何通过 ROS 轻松实现资源编排新方式
    我在阿里写代码学会的六件事
    SpringCloud 应用在 Kubernetes 上的最佳实践 — 诊断(线上联调)
    视频需求超平常数 10 倍,却节省了 60% 的 IT 成本投入是一种什么样的体验?
    从单体到混乱的微服务,阿里云托管式服务网格是如何诞生的?
    阿里张磊:如何构建以应用为中心的“Kubernetes”?(内含 QA 整理)
    python之深度学习-模拟异步操作(队列)
    python之深度学习-队列处理数据(同步)
    python深度学习-tensorflow实现一个线性回归的案例
  • 原文地址:https://www.cnblogs.com/Foreign/p/15313973.html
Copyright © 2020-2023  润新知