【比赛】东方博宜oj 25年10月-C组(大咖)解析A-B

【比赛】东方博宜oj 25年10月-C组(大咖)解析

A.弹跳游戏

题目描述
在一款弹跳游戏中,游戏主人公可以在屏幕上按照固定的距离 L 在游戏屏幕上向左或者向右跳跃。游戏屏幕可以简化为一个数轴,如果游戏主人公位于位置 X,则其向左跳跃 1 次可以到达X−L 的位置,向右跳跃 1 次可以到达 X+L 的位置。游戏中共有 N 枚金币,第 i 枚金币放置在位置 P i处,游戏主人公主只要跳跃到存在金币的位置,就能取走该位置的金币,该位置的金币被取走后,不会重新产生新的金币。游戏主人公从最初位置 X 出发,可以向左或向右进行任意多次跳跃,跳跃距离固定为 L。请你编程计算出:如果游戏主人公想要取走所有的金币,那么他的固定跳跃距离 L 的最大值是多少?
输入
第一行包含两个整数 N,X,分别表示金币的数量、游戏主人公的初始位置。第二行包含 N 个整数 P 1​ ,P 2​ ,…,P N ,表示每个金币的位置。
输出
输出一个整数,表示在能取走所有金币的前提下,最大的跳跃距离 L。显而易见的是,L=1 的情况下,一定能取走所有位置的金币。
样例
输入
3 3
1 7 11
输出
2
输入
3 81
33 105 57
输出
24
输入
1 1
1000000000
输出
999999999
说明:
样例 1说明
设置跳跃距离为 L=2,可以实现目标。
从位置 3 出发,向左跳跃 1 次,可以到达位置 1。
从位置 1 出发,向右跳跃 3 次,可以到达位置 7。
从位置 7 出发,向右跳跃 2 次,可以到达位置 11。
数据范围
对于 100% 的数据,满足 1≤N≤105,1≤X≤109 ,1≤P i≤10^9 。且保证,所有的 P i互不相同,X≠P i 。
10% 的数据 N=1
另外 30%的数据 X,P i ≤1000


让我们先一步步分析这个问题。

  • 主人公初始位置是 X。
  • N N N 枚金币,位置分别是 P 1 , P 2 , … , P N P_1, P_2, \dots, P_N P1,P2,,PN
  • 主人公可以向左或向右跳,每次跳固定的距离 L L L
  • 跳到金币位置就能收集金币,金币被取走后消失。
  • 问:能收集所有金币的前提下,最大的 L L L 是多少。

如果主人公能跳到某个金币位置 ( P_i ),那么他能跳到的位置是: X + k ⋅ L X + k \cdot L X+kL
其中 k k k 是任意整数(正、负、零)。
也就是说,主人公能到达的位置集合是: { X + k L ∣ k ∈ Z } \{ X + kL \mid k \in \mathbb{Z} \} {X+kLkZ}
要收集所有金币,必须保证每个 ( P_i ) 都在这个集合里,即: P i ≡ X ( m o d L ) P_i \equiv X \pmod{L} PiX(modL)
换句话说: L ∣ ( P i − X ) L \mid (P_i - X) L(PiX)
L L L 必须整除每个 ( P i − X ) (P_i - X) (PiX) 的绝对值。
我们可以设 d i = ∣ P i − X ∣ d_i = |P_i - X| di=PiX
那么条件就是: L ∣ d 1 , L ∣ d 2 , … , L ∣ d N L \mid d_1, L \mid d_2, \dots, L \mid d_N Ld1,Ld2,,LdN
L L L 是所有 d i d_i di 的公约数。
我们要找的是 最大 L L L,所以 L L L 应该是所有 d i d_i di最大公约数。因为:

  • 如果 L L L 是这些差的公约数,那么主人公可以到达所有金币位置。
  • 如果 L L L 是它们的最大公约数,那么它是最大的可行 L L L
  • 因为如果 L L L更大,它可能无法整除某个 d i d_i di,那么该金币就无法到达。
    所以:
    L max ⁡ = gcd ⁡ ( ∣ P 1 − X ∣ , ∣ P 2 − X ∣ , … , ∣ P N − X ∣ ) L_{\max} = \gcd(|P_1 - X|, |P_2 - X|, \dots, |P_N - X|) Lmax=gcd(P1X,P2X,,PNX)
    样例 1
    N = 3 , X = 3 , P = [ 1 , 7 , 11 ] N=3, X=3, P=[1,7,11] N=3,X=3,P=[1,7,11]
    差: ∣ 1 − 3 ∣ = 2 , ∣ 7 − 3 ∣ = 4 , ∣ 11 − 3 ∣ = 8 |1-3|=2, |7-3|=4, |11-3|=8 ∣13∣=2,∣73∣=4,∣113∣=8
    g c d ( 2 , 4 , 8 ) = 2 gcd(2,4,8) = 2 gcd(2,4,8)=2
    样例 2
    N = 3 , X = 81 , P = [ 33 , 105 , 57 ] N=3, X=81, P=[33,105,57] N=3,X=81,P=[33,105,57]
    差: ∣ 33 − 81 ∣ = 48 , ∣ 105 − 81 ∣ = 24 , ∣ 57 − 81 ∣ = 24 |33-81|=48, |105-81|=24, |57-81|=24 ∣3381∣=48,∣10581∣=24,∣5781∣=24
    g c d ( 48 , 24 , 24 ) = 24 gcd(48,24,24) = 24 gcd(48,24,24)=24
    样例 3
    N = 1 , X = 1 , P = [ 1 0 9 ] N=1, X=1, P=[10^9] N=1,X=1,P=[109]
    差: 1 0 9 − 1 10^9 - 1 1091
    KaTeX parse error: Can't use function '\(' in math mode at position 5: gcd(\̲(̲ 10^9 - 1 \)) =…
    所以程序要做到:
  • 读入 N , X N, X N,X P P P 数组。
  • 计算每个 ∣ P i − X ∣ |P_i - X| PiX
  • 求这些差值的最大公约数。
  • 输出 g c d gcd gcd
    时间复杂度: O ( N + log ⁡ ( max ⁡ P ) ) O(N + \log(\max P)) O(N+log(maxP))
    上代码!!!!!
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

long long gcd(long long a, long long b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}

int main() {
    int N;
    long long X;
    cin >> N >> X;
    
    vector<long long> P(N);
    for (int i = 0; i < N; i++) {
        cin >> P[i];
    }
    
    vector<long long> diffs(N);
    for (int i = 0; i < N; i++) {
        diffs[i] = llabs(P[i] - X);
    }
    
    long long ans = diffs[0];
    for (int i = 1; i < N; i++) {
        ans = gcd(ans, diffs[i]);
    }
    
    cout << ans << endl;
    
    return 0;
}

B. 换座位

题目描述
一年级一班的教室里,有 N 个座位,座位编号为 1⋯N。有N 名同学学号为 1⋯N。开学当天,老师让第 i 名学生坐在第 i 号座位上。经过了几天的课堂学习,同学们互相熟悉了,产生了换座位的想法。每位同学给老师都写了一份自己的喜欢的座位偏好表,第 i 位同学提交的座位偏好表中有 N 个座位编号:A 1 ,A 2 ,…A n,表示该同学最希望坐到编号为 A 1的座位,如果无法实现,则第二喜欢的座位编号为 A 2 …老师收到了所有同学提交的座位偏好表之后,想请爱好编程的你,帮助同学们重新调整座位。在调整结束后,要保证每名学生最终的座位要么和原来的一样,要么是自己偏好顺序表中更靠前的座位。请编程计算出在合理的重新分配之后,每位同学有可能得到的最好的座位编号。
输入
第一行输入一个整数 N。接下来 N 行,每行包含 N 个整数:A 1 ,A2 ,…A n ,保证这是一个 1⋯N 的排列,表示对应同学的座位偏好顺序表。
输出
N 行,第 i 行输出学生 i 在重新分配后有可能得到的最好的座位编号。
样例
输入
4
1 2 3 4
1 3 2 4
1 2 3 4
1 2 3 4
输出
1
3
2
4
输入
6
1 2 3 4 5 6
3 4 5 6 1 2
4 5 6 1 2 3
5 6 1 2 3 4
6 1 2 3 4 5
2 3 4 5 6 1
输出
1
3
4
5
6
2
输入
8
4 1 6 7 2 5 8 3
3 2 4 7 5 1 6 8
8 4 7 1 3 6 2 5
7 8 1 3 4 6 2 5
5 2 7 1 3 8 6 4
1 5 6 3 4 7 2 8
7 3 8 5 4 6 2 1
4 7 1 2 6 5 8 3
输出
4
3
8
8
5
1
7
4
说明:
样例 1 说明
在这个例子中:
初始安排:学生 1 坐 1 号座位,学生 2 坐 2 号座位,学生 3 坐 3 号座位,学生 4 坐 4 号座位。
调整后的安排:学生 1 仍然坐 1 号座位,学生 2 换到 3 号座位,学生 3 换到 2 号座位,学生 4 仍然坐 4 号座位。可以看到,学生 1 和 4 没法得到更靠前的选择,而学生 2 和 3 都能换到自己更喜欢的座位。
数据范围
对于 20% 的数据,满足 N≤8。
对于 100% 的数据,满足 1≤N≤500。


让我们看一看题目:

  • 初始:学生 i i i 坐在座位 i i i
  • 每个学生有一个偏好列表,是一个 1 … N 1 \dots N 1N 的排列,表示他最喜欢的座位、第二喜欢的座位 … … …… ……直到第 N N N 喜欢的座位。
  • 最终调整座位后,每个学生要么坐在原座位,要么坐在偏好列表中 比原座位更靠前 的座位(即更喜欢的座位)。
  • 要求:对于每个学生 i i i,输出在 合理分配 下,他能得到的最好的座位编号。
    “合理分配” 意味着:
  • 不能有两个学生坐在同一个座位。
  • 每个学生的新座位必须在他的偏好列表中 比原座位更靠前或等于原座位 的位置(即要么不变,要么换到更喜欢的座位)。
    分析样例:
    样例 1
4
1 2 3 4
1 3 2 4
1 2 3 4
1 2 3 4

初始座位: 1 → 1 , 2 → 2 , 3 → 3 , 4 → 4 1→1, 2→2, 3→3, 4→4 11,22,33,44

  • 学生1:最喜欢1号座位,本来就在1号,所以最好就是1。
  • 学生2:最喜欢1号,但1号已被学生1占着(学生1不会让,因为1号是他最喜欢的),第二喜欢3号,3号目前是学生3坐着,学生3可以换到2号(因为学生3的偏好中2号比3号靠前吗?检查:学生3的偏好是 [ 1 , 2 , 3 , 4 ] [1,2,3,4] [1,2,3,4],2号是第2喜欢,3号是第3喜欢,所以可以换到更靠前的2号),这样学生2就能得到3号。
  • 学生3:本来坐3号,现在换到2号,2号比3号靠前,所以可以换。
  • 学生4:偏好 [ 1 , 2 , 3 , 4 ] [1,2,3,4] [1,2,3,4],4号是原座位,没有更靠前的可选,所以保持4号。
    输出:
1
3
2
4

这是一个经典的 “每个人有一个可接受集合,求最优完美匹配” 问题,可以用 匹配 + 枚举 解决。
已知解法:

  • 用匈牙利算法求完美匹配,但只考虑学生 i i i 和偏好列表中从第 1 1 1 个到原座位位置的那些座位之间的边。
  • 为了让学生 i i i 得到尽可能好的座位,我们按偏好顺序尝试:
    • 加入学生 i i i 与偏好座位 j j j 的边。
    • 检查图中是否存在完美匹配。
    • 如果存在,说明学生 i i i 可以坐在 j j j,且其他人也能合法安排。
    • 于是 j j j 就是学生 i i i 能得到的最终最好座位。
      对每个学生 i i i 做一次,每次 O ( N 3 ) O(N^3) O(N3),总 O ( N 4 ) O(N^4) O(N4) N = 500 N=500 N=500 时不可行。
      但可以优化:二分学生 i i i 的偏好位置,检查完美匹配, O ( N 3 log ⁡ N ) O(N^3 \log N) O(N3logN),可行。
      让我们理一理思路:
      对每个学生 i i i
  • 在偏好列表中,二分找到最好的座位位置 p o s pos pos,使得:
    • 图中包含:所有学生 k 与原座位之间的边(作为保底),并且学生 i 与偏好前 pos 个座位有边,其他学生 k 与偏好前(原座位位置)的座位有边。
    • 存在完美匹配。
  • 这个座位就是答案。
    实现时,可以:
  1. 构建初始二分图:每个学生 k k k 与偏好列表中前 r a n k [ k ] [ k ] rank[k][k] rank[k][k] 个座位有边( r a n k [ k ] [ k ] rank[k][k] rank[k][k] 是原座位在偏好列表中的位置,从 1 开始计数)。
  2. 对学生 i i i,二分 m m m:学生 i i i 只与偏好前 m m m 个座位有边,检查完美匹配。
  3. 找到最小的 m 使得有完美匹配,对应座位就是 p r e f [ i ] [ m − 1 ] pref[i][m-1] pref[i][m1]
    复杂度: O ( N 3 log ⁡ N ) O(N^3 \log N) O(N3logN) N = 500 N=500 N=500 可过。
    上代码!!!!!
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int N;
vector<vector<int>> pref;
vector<int> rankOfOriginal; // rankOfOriginal[i] 原座位 i 在学生 i 的偏好列表中的位置(从1开始)

// 匈牙利算法找完美匹配
bool dfs(const vector<vector<int>>& graph, int u, vector<bool>& visited, vector<int>& match) {
    for (int v : graph[u]) {
        if (!visited[v]) {
            visited[v] = true;
            if (match[v] == -1 || dfs(graph, match[v], visited, match)) {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

// 检查是否存在完美匹配
bool hasPerfectMatch(const vector<vector<int>>& graph) {
    vector<int> match(N, -1); // match[seat] = student
    int count = 0;
    for (int u = 0; u < N; u++) {
        vector<bool> visited(N, false);
        if (dfs(graph, u, visited, match)) {
            count++;
        }
    }
    return count == N;
}

int main() {
    cin >> N;
    pref.resize(N, vector<int>(N));
    rankOfOriginal.resize(N);
    
    // 读入数据
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> pref[i][j];
            pref[i][j]--; // 转为0-based
            if (pref[i][j] == i) {
                rankOfOriginal[i] = j + 1; // 1-based
            }
        }
    }
    
    vector<int> ans(N);
    
    // 对每个学生找最好的座位
    for (int i = 0; i < N; i++) {
        // 二分学生 i 的偏好位置
        int low = 1, high = rankOfOriginal[i];
        int best = rankOfOriginal[i];
        
        while (low <= high) {
            int mid = (low + high) / 2;
            
            // 构建二分图
            vector<vector<int>> graph(N);
            for (int k = 0; k < N; k++) {
                int limit = (k == i) ? mid : rankOfOriginal[k];
                for (int t = 0; t < limit; t++) {
                    graph[k].push_back(pref[k][t]);
                }
            }
            
            if (hasPerfectMatch(graph)) {
                best = mid;
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        
        ans[i] = pref[i][best - 1] + 1; // 转回1-based输出
    }
    
    // 输出结果
    for (int i = 0; i < N; i++) {
        cout << ans[i] << endl;
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

my小天神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值