2020.9.19 ICPC网络选拔赛 第一场

A Busiest Computing Nodes

2020.9.19 ICPC网络选拔赛 第一场

题意

有 k k k台机器和 n n n条指令。对于一条指令(编号为 i i i)来说,如果它到的时候,所有的机器都在完成其他的指令,那么这条指令作废;否则,从第 i i%k i个机器开始从前往后扫描,遇到第一个空闲的机器执行这条指令。请求出执行指令最多的机器,如果有多个,按照序号升序输出。

思路

首先,我们要判断指令到达的时候是否全部机器都在执行指令,那么我们就需要记录所有机器最早什么时候才能完成工作(即最早的指令完成时间),可以用 s e t + m a p set+map set+map进行记录, s e t set set可以自动排序,随时可以找到最早的时间, m a p map map记录每个时间的个数,因为可以有多台机器在同一时间完成指令。当 m a p map map所对应的时间的个数为0时,需要在set里把该时间删掉。
然后,就是如何快速找到第一个空闲的机器,如果直接线性搜索的话,很有可能TLE,那么我们可以通过线段树去存储每个机器的结束时间和区间的最小值,(注意:线段树从1开始,而机器从0开始,需要+1匹配),并且开一个 2 ∗ k 2*k 2∗k的线段树,找的时候直接找 [ i % k + 1 , i % k + k ] [i\%k+1,i\%k+k] [i%k+1,i%k+k]区间(已经+1)即可,然后通过二分找到第一个空闲的机器。

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-20 10:28:45
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-20 10:49:00
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.19\A.cpp
 */
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int a[N << 2], tr[N << 3], cnt[N];
set<int> s;
map<int, int> m;

void bulid(int i, int l, int r)
{
    if (l == r) {
        tr[i] = 0x3f3f3f3f;
        return;
    }
    int mid = l + r >> 1;
    bulid(i << 1, l, mid);
    bulid(i << 1 | 1, mid + 1, r);
    tr[i] = min(tr[i << 1], tr[i << 1 | 1]);
}

void update(int i, int l, int r, int x, int y)
{
    if (l > x || x > r)
        return;
    if (l == r && l == x) {
        tr[i] = y;
        return;
    }
    int mid = l + r >> 1;
    update(i << 1, l, mid, x, y);
    update(i << 1 | 1, mid + 1, r, x, y);
    tr[i] = min(tr[i << 1], tr[i << 1 | 1]);
}

int query(int i, int l, int r, int x, int y)
{
    if (y < l || x > r)
        return 0x3f3f3f3f;
    if (l >= x && r <= y)
        return tr[i];
    int mid = l + r >> 1;
    return min(query(i << 1, l, mid, x, y), query(i << 1 | 1, mid + 1, r, x, y));
}

int f(int l, int r, int x, int k)
{
    if (l == r)
        return l;
    int mid = (l + r) / 2;
    if (query(1, 1, 2 * k, l, mid) < x)//为了查到第一个,需要先查左边
        f(l, mid, x, k);
    else
        f(mid + 1, r, x, k);
}

int main()
{
    int k, n;
    cin >> k >> n;
    bulid(1, 1, 2 * k);
    s.insert(0);
    m[0] = k;
    for (int i = 0; i < n; ++i) {
        int l, time;
        cin >> l >> time;
        if (*s.begin() >= l)
            continue;
        if (a[i % k] < l) {
            cnt[i % k]++;
            m[a[i % k]]--;
            if (m[a[i % k]] == 0)
                s.erase(a[i % k]);
            a[i % k] = l + time - 1;
            if (s.find(a[i % k]) == s.end())
                s.insert(a[i % k]);
            m[a[i % k]]++;
            //同时更新i%k和i%k+k
            update(1, 1, 2 * k, (i % k) + 1, l + time - 1);//在线段树上+1
            update(1, 1, 2 * k, (i % k) + k + 1, l + time - 1);//在线段树上+1
        } else {
            int id = f(i % k + 1, i % k + k, l, k);
            id--;
            id %= k;
            cnt[id]++;
            m[a[id]]--;
            if (m[a[id]] == 0)
                s.erase(a[id]);
            a[id] = l + time - 1;
            if (s.find(a[id]) == s.end())
                s.insert(a[id]);
            m[a[id]]++;
            update(1, 1, 2 * k, id + 1, l + time - 1);
            update(1, 1, 2 * k, id + k + 1, l + time - 1);
        }
    }
    int minx = 0, ans = 0;
    for (int i = 0; i < k; i++)
        minx = max(minx, cnt[i]);
    for (int i = 0; i < k; i++) {
        if (cnt[i] == minx) {
            if (ans)
                printf(" ");
            printf("%d", i);
            ans++;
        }
    }
    return 0;
}

ps.队友打线段树板子的时候疏忽了,query那里有个&&打成了||,导致我们晚了快一个小时才AC,太难了~~

F Land Overseer

2020.9.19 ICPC网络选拔赛 第一场

题意

平面直角坐标系中有三个点, O ( 0 , 0 ) , A ( a , b ) , B ( 2 × a , 0 ) O(0,0),A(a,b),B(2\times a,0) O(0,0),A(a,b),B(2×a,0),现在需要从 O O O点出发,经过以 A A A和 B B B为圆心, r r r为半径的圆。求最短路径。

思路

如果圆足够大,使得圆能覆盖的 x x x轴,则直接走向 B B B,距离为 2 × a − r 2\times a-r 2×a−r
如果不够大,则先走到以 A A A为圆心的圆的最低点,然后再径直走向 B B B,距离为 ( a − 0 ) 2 + ( b − r ) 2 + ( 2 × a − a ) 2 + ( 0 − ( b − r ) ) 2 − r = 2 × a 2 + ( b − r ) 2 − r \sqrt{(a-0)^{2}+(b-r)^{2}}+\sqrt{(2\times a-a)^{2}+(0-(b-r))^{2}-r}=2\times \sqrt{a^{2}+(b-r)^{2}}-r (a−0)2+(b−r)2 ​+(2×a−a)2+(0−(b−r))2−r ​=2×a2+(b−r)2 ​−r

/*
 * @Author: Icey_dying
 * @Date: 2021-09-20 10:29:01
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-20 11:06:50
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.19\F.cpp
 */
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int a[N];

int main()
{
    int t;
    cin >> t;
    for (int i = 1; i <= t; ++i) {
        double a, b, r;
        cin >> a >> b >> r;
        if (r >= b) {
            printf("Case #%d: %.2f\n", i, 2 * a - r);
        } else {
            printf("Case #%d: %.2f\n", i, 2 * sqrt(a * a + (b - r) * (b - r)) - r);
        }
    }
    return 0;
}

H Mesh Analysis

2020.9.19 ICPC网络选拔赛 第一场
2020.9.19 ICPC网络选拔赛 第一场

题意

三维空间有一些点,并且还有三角网和线段,对于属于同一个三角网和同一条线段的点,互为相邻点,求每个点的相邻点和属于哪个三角网或线段。

思路

这题应该算是比较简单的题了,仅次于F。直接用set存相邻点和所属标号即可,注意输入的都是id,要用map存一下每个编号对应的是第几个点。

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-20 10:29:24
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-20 10:29:25
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.19\H.cpp
 */
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, m;
set<int> s1[N], s2[N];
map<int, int> map1;
void insert111(int a, int b)
{
    if (s1[map1[a]].find(b) == s1[map1[a]].end())
        s1[map1[a]].insert(b);
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    int id, biao, a, b, c;
    double x, y, z;
    for (int i = 1; i <= n; i++) {
        cin >> id;
        map1[id] = i;
        cin >> x >> y >> z;
        // printf("%lf %lf %lf\n", x, y, z);
    }
    for (int i = 1; i <= m; i++) {
        cin >> id >> biao;
        switch (biao) {
        case 102:
            cin >> a >> b;
            insert111(a, b); insert111(b, a);
            s2[map1[a]].insert(id);
            s2[map1[b]].insert(id);
            break;
        case 203:
            cin >> a >> b >> c;
            insert111(a, b); insert111(a, c);
            insert111(b, c); insert111(b, a);
            insert111(c, a); insert111(c, b);
            s2[map1[a]].insert(id);
            s2[map1[b]].insert(id);
            s2[map1[c]].insert(id);
        }
    }
    int t;
    cin >> t;
    while (t--) {
        cin >> id;
        if (map1[id] == 0) {
            printf("%d\n[]\n[]\n", id);
        } else {
            printf("%d\n[", id);
            for (auto i : s1[map1[id]]) {
                if (i != *s1[map1[id]].begin())
                    printf(",");
                printf("%d", i);
            }
            printf("]\n[");
            for (auto i : s2[map1[id]]) {
                if (i != *s2[map1[id]].begin())
                    printf(",");
                printf("%d", i);
            }
            printf("]\n");
        }
    }
    return 0;
}

I Neighborhood Search

2020.9.19 ICPC网络选拔赛 第一场

题意

给出S里面的点,给出A的值和r,求出S中点的值与A的差值小于等于r的点,降序输出

思路

直接找就行,输入对于C++有些麻烦,队友直接用Python写出来了

代码

lst = list(map(int, input().split()))
x = int(input())
r = int(input())
lst.sort()
flag = 0
for i in range(len(lst) - 1, -1, -1):
    if abs(lst[i] - x) <= r:
        print(lst[i], end=" ")
        flag = 1
if flag == 0:
    print()

K Segment Routing

2020.9.19 ICPC网络选拔赛 第一场

题意

给出一些点和它们的边(有编号),再给出一些路径,看是否能到达另外的点,如果可以,输出该点的编号,否则输出"Packet Loss"

思路

用vector记录边即可,注意判断"Packet Loss"的时候,不要直接break,那样会让数据输入产生混乱,我们就是因为这个没有AC

代码

/*
 * @Author: Icey_dying
 * @Date: 2021-09-20 10:30:11
 * @LastEditors: Icey_dying
 * @LastEditTime: 2021-09-20 11:22:06
 * @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.19\K.cpp
 */
#include <bits/stdc++.h>
using namespace std;
int n, m;
const int N = 1e5 + 50;
vector<int> v[N];

int main()
{
    int t;
    scanf("%d", &t);
    for (int k = 1; k <= t; k++) {
        scanf("%d%d", &n, &m);
        int xx, u;
        for (int i = 1; i <= n; i++) {
            v[i].clear();
            cin >> xx;
            for (int j = 1; j <= xx; j++) {
                scanf("%d", &u);
                v[i].push_back(u);
            }
        }
        printf("Case #%d: \n", k);
        for (int j = 1; j <= m; j++) {
            bool flag = 0;
            scanf("%d %d", &u, &k);
            int vv = u, id;
            for (int i = 1; i <= k; i++) {
                scanf("%d", &id);
                if (id > v[vv].size() || id == 0)
                    flag = 1;
                if (flag == 1)
                    continue;

                vv = v[vv][id - 1];
            }
            if (flag)
                printf("Packet Loss\n");
            else {
                printf("%d\n", vv);
            }
        }
    }
    return 0;
}

上一篇:python 简单字符串字典加密


下一篇:机器人塔(蓝桥杯)