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

Posted 人形自走Bug生成器

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ICPC第一场网络赛 题解 + 补题相关的知识,希望对你有一定的参考价值。

一些闲话旅行传送门

A. Busiest Computing Nodes

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

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

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

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

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

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

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

AC代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN = 2e5 + 10;

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()
{
    scanf("%lld%lld", &k, &n);
    for (int i = 1; i <= n; i++)
        scanf("%lld%lld", &a[i].ar, &a[i].pro), 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\\n", realans[realans.size() - 1]);
    return 0;
}

F. Land Overseer

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

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

AC代码

#include <bits/stdc++.h>
typedef long long ll;
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) || (a <= R))
            ans = sqrt(4.0 * a * a + 4.0 * (b - R) * (b - R)) - 1.0 * R;
        else if (a > R && b <= R)
            ans = 2.0 * a - 1.0 * R;
        printf("Case #%d: %.2lf\\n", i, ans);
    }
    return 0;
}

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

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

AC代码

#include <bits/stdc++.h>
typedef long long ll;
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 + 1 + len, 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;
}

K. Segment Routing

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

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

AC代码

#include <bits/stdc++.h>
using ll = long long;
using namespace std;
const int maxn = 1e5 + 5;
std::vector<int> e[maxn];

int main()
{
    int T;
    scanf("%d", &T);
    for (int cas = 1; cas <= T; cas++)
    {
        printf("Case #%d: \\n", cas);
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            e[i].clear();
        for (int i = 1; i <= n; i++)
        {
            int d;
            scanf("%d", &d);
            for (int j = 1; j <= d; j++)
            {
                int v;
                scanf("%d", &v);
                e[i].push_back(v);
            }
        }
        for (int i = 1; i <= m; i++)
        {
            int u, l;
            scanf("%d%d", &u, &l);
            int flag = 1;
            for (int j = 1; j <= l; j++)
            {
                int v;
                scanf("%d", &v);
                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;
}

以上是关于ICPC第一场网络赛 题解 + 补题的主要内容,如果未能解决你的问题,请参考以下文章

日常补题——ICPC网络赛上海站第二题B Light bulbs

J Red-Black Paths(ICPC网络赛第一场)

J Red-Black Paths(ICPC网络赛第一场)

2021 ICPC网络赛第一场 A set+优先队列

线段树-维护区间最小值和区间和2021 ICPC网络赛第一场 D: Edge of Taixuan

线段树-维护区间最小值和区间和2021 ICPC网络赛第一场 D: Edge of Taixuan