题解:洛谷 P9976 Farmer John Actually Farms

【题目来源】

洛谷:P9976 [USACO23DEC] Farmer John Actually Farms B - 洛谷

【题目描述】

Farmer John 在他的农场上种植了 \(N\)\(1 \leq N \leq 2\cdot 10^5\))株芦笋!然而,其中有一些植物存在基因差异,长得比其他植物快。第 \(i\) 株植物的初始高度为 \(h_i\) 英寸,之后每天,第 \(i\) 株植物长高 \(a_i\) 英寸。

FJ 更加钟爱其中的一些植物。他将给你一组由不同整数组成的数组 \(t_1,\dots,t_N\),这个数组包含 \(0\)\(N-1\) 的全部整数。他希望恰好有 \(t_i\) 株植物比第 \(i\) 株植物高。找到最少要经过多少天,才能满足 FJ 的要求,或者报告这个要求是不可能满足的。

【输入】

每个测试点中包含多组测试数据。

第一行为一个整数 \(T\),代表测试数据组数(\(1 \leq T \leq 10\))。

对于每一组测试数据,第一行一个整数 \(N\)\(1 \leq N \leq 2\cdot 10^5\)),表示植物数量。

第二行包含 \(N\) 个整数 \(h_i\)\(1 \leq h_i \leq 10^9\)),表示第 \(i\) 株植物的初始高度。

第三行包含 \(N\) 个整数 \(a_i\)\(1 \leq a_i \leq 10^9\)),表示第 \(i\) 株植物每天增长的高度。

第四行包含 \(N\) 个不同的整数 \(t_i\),表示 FJ 给你的数组。

保证所有测试数据的 \(N\) 的和不超过 \(2\cdot 10^5\)

【输出】

输出 \(T\) 行,每行表示一组测试数据的答案。如果要求不可能满足,输出 \(-1\)

请注意,由于这个问题涉及的整数大小较大,可能需要使用 64 位整数数据类型(例如,在 C/C++ 中使用 long long 类型)。

【输入样例】

6
1
10
1
0
2
7 3
8 10
1 0
2
3 6
10 8
0 1
2
7 3
8 9
1 0
2
7 7
8 8
0 1
2
7 3
8 8
1 0

【输出样例】

0
3
2
5
-1
-1

【核心思想】

  1. 问题分析:有 \(N\) 株植物,每株初始高度 \(h_i\),每日生长 \(a_i\)。给定目标排名数组 \(t_i\)\(t_i\) 表示最终恰好有 \(t_i\) 株植物比第 \(i\) 株高),求满足所有植物排名要求的最小天数 \(d\),或判断不可能。由于 \(t_i\)\(0 \sim N-1\) 的一个排列,因此排名要求等价于:按最终高度从低到高排序后,每株植物的位置必须等于 \(t_i\)。也就是说,最终高度的相对顺序是固定的(由 \(t_i\) 决定)。问题转化为:找到最小的 \(d\),使得对所有植物按 \(t_i\) 排序后,其高度 \(H_i(d) = h_i + a_i \cdot d\) 严格递增。

  2. 算法选择

    • 排序 + 不等式约束:将植物按 \(t_i\) 从小到大排序(即最终高度从低到高的顺序)。对于排序后相邻的两株植物 \(i\)\(i+1\),必须满足 \(H_i(d) < H_{i+1}(d)\)。这给出了关于 \(d\) 的不等式,每个不等式形如 \((a_i - a_{i+1}) \cdot d < h_{i+1} - h_i\)。根据不同情况(\(a_i\)\(a_{i+1}\) 的大小),可以得到 \(d\) 的下界或上界,或无解。最终取所有下界的最大值和所有上界的最小值,若下界 \(\le\) 上界则可行,最小天数为下界(若为负数则取 0)。
  3. 关键步骤(基于代码逻辑):

    • 读入并排序:将植物按 \(t\) 值从小到大排序(排序后,\(t_i\) 即为索引位置)。
    • 初始化maxn = 0(下界),minn = INF(上界)。
    • 遍历相邻对:对于 \(i\) 从 1 到 \(N-1\),设 \(a = h_i, b = a_i, c = h_{i+1}, d = a_{i+1}\)。需要满足 \(a + b \cdot x < c + d \cdot x\),即 \((b - d) \cdot x < c - a\),其中 \(x\) 为天数。
      • \(b == d\):则不等式变为 \(0 < c - a\)。若 \(a \ge c\),则永远无法满足,输出 -1;若 \(a < c\),则恒成立。
      • \(b > d\)\(x < \frac{c - a}{b - d}\) 且需满足 \(a < c\) 才能在某天追上。计算下界 \(x = \lfloor (c-a)/(b-d) \rfloor + 1\),更新 maxn
      • \(b < d\)\(x > \frac{c - a}{d - b}\),若 \(a > c\),则计算上界 \(x = \lfloor (a-c-1)/(d-b) \rfloor\),更新 minn;若 \(a \le c\),则 \(i\) 永远比 \(i+1\) 矮,正符合排序要求,无额外约束。
    • 判断可行性:若 maxn > minn,则无解输出 -1;否则输出 maxn(最小天数)。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),主要来自排序。
    • 空间复杂度:\(O(N)\)
  5. 排序 + 不等式约束的核心思想

    • 排名转顺序:将每株植物的目标排名 \(t_i\) 看作其最终在高度排序中的位置。按 \(t_i\) 排序后,相邻植物的高度必须严格递增。
    • 线性函数比较:每株植物高度是 \(d\) 的一次函数 \(h_i + a_i d\)。两个一次函数的大小关系随 \(d\) 变化最多改变一次,因此约束可转化为 \(d\) 的区间(下界或上界)。
    • 区间交:所有约束的交集即为可行天数集合。若交集非空,最小天数即为下界的最大值(至少为 0)。
    • 严格不等式的整数处理:对于 \(a + b x < c + d x\),移项后需注意除法和取整方向,确保严格不等号成立。
    • 适用场景:适用于多个线性增长实体,要求其在某个时间点满足特定排序,且排名由 \(t\) 数组给定。核心是利用线性函数的单调性转化为区间约束。

【解题思路】

【算法标签】

普及 #数学

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int T, n;
struct node {
    long long h, a, t;
}p[200005];
bool cmp (node x, node y){
    return x.t<y.t;
}
int main()
{
    cin >> T;  // 输入T
    while (T--) {  // 遍历T次询问
        cin >> n;  // 输入n
        for (int i=1; i<=n; i++) cin >> p[i].h;  // 使用结构体数组,记录每个植物的h、a和t
        for (int i=1; i<=n; i++) cin >> p[i].a;
        for (int i=1; i<=n; i++) cin >> p[i].t;
        if (n==1) {  // 如果n==1特判,输出0
            cout << 0 << endl;
            continue;
        }
        int minn=1e9, maxn=-1e9;  // 定义满足条件的最大值和最小值
        sort(p+1, p+n+1, cmp);  // 按照t从小到大方式排序
        int mark=0;  // 定义标记位
        for (int i=1; i<n; i++) {  // 遍历n-1个植物
            int a=p[i].h, b=p[i].a, c=p[i+1].h, d=p[i+1].a;  // 定义变量简化代码长度
            if (b==d) {  // 当b==d时
                if (a<=c) {  // 如果a小于等于c,永远无法追上,输出-1
                    cout << -1 << endl;
                    mark = 1;
                    break;
                } else {  // 否则,只需0天就可以满足
                    maxn = max(maxn, 0);
                }
            }
            if (b>d) {  // 当b>d时
                if (a<=c) {  // 如果a小于等于c,则在某天之后就一直超越
                    int x = (c-a)/(b-d)+1;  // 相减后相除的结果是相等的情况,还需要再加1
                    maxn = max(maxn, x);
                } else {  // 否则,只需0天就可以满足
                    maxn = max(maxn, 0);
                }
            }
            if (b<d) {  // 当b<d时
                if (a<=c) {  // 如果a小于等于c,永远无法追上,输出-1
                    cout << -1 << endl;
                    mark=1;
                    break;
                } else {
                    int x = (a-c-1)/(d-b);  // 否则开始超越,但到某天后就不再满足
                    maxn = max(maxn, 0);
                    minn = min(minn, x);  // 记录该minn
                }
            }
        }
        if (mark==1) continue;
        if (maxn>minn) {  // 要求maxn>minn,即满足条件maxn < x < minn,才有结果输出,否则输出-1
            cout << -1 << endl;
            continue;
        } else {
            cout << maxn << endl;
        }
    }
    return 0;
}

【运行结果】

6
1
10
1
0
0
2
7 3
8 10
1 0
3
2
3 6
10 8
0 1
2
2
7 3
8 9
1 0
5
2
7 7
8 8
0 1
-1
2
7 3
8 8
1 0
-1
posted @ 2026-08-30 09:20  团爸讲算法  阅读(7)  评论(0)    收藏  举报