题解:洛谷 P9010 Leaders
【题目来源】
洛谷:P9010 [USACO23JAN] Leaders B - 洛谷
【题目描述】
农夫约翰有 \(N\) 头牛 \((2 \le N \le 10^5)\)。每头牛的品种要么是 Guernsey,要么是 Holstein。通常情况下,牛站成一排,按顺序编号为 \(1 \cdots N\)。
在一天的过程中,每头牛都会写下一个牛的列表。具体来说,第 \(i\) 头牛的列表包含从她自己(第 \(i\) 头牛)开始到包括第 \(E_i(i \le E_i \le N)\) 头牛的范围。
FJ 最近发现,每种牛的品种都有一个独特的领导者。FJ 不知道领导者是谁,但他知道每个领导者的列表必须包括其品种的所有牛,或者其他品种的领导者(或两者)。
帮助 FJ 计算可能成为领导者的牛对数。保证至少有一对可能的领导者。
【输入】
第一行包含 \(N\)。
第二行包含一个长度为 \(N\) 的字符串,其中第 \(i\) 个字符表示第 \(i\) 头牛的品种(G 表示 Guernsey,H 表示 Holstein)。保证至少有一个 Guernsey 和一个 Holstein。
第三行包含 \(E_1 \cdots E_N\)。
【输出】
输出可能的领导者对数。
【输入样例】
4
GHHG
2 4 3 4
【输出样例】
1
【核心思想】
-
问题分析:给定 \(N\) 头牛排成一排,每头牛 \(i\) 的列表覆盖区间 \([i, E_i]\)。每头牛有品种 G 或 H。定义一个牛为“领导者”,当且仅当它的列表包含该品种所有牛,或包含另一品种的领导者(包含自身品种所有牛是被包含于另一品种领导者列表的充分条件)。求有多少对 \((i, j)\) 使得两头牛(品种不同)都能成为领导者。本质是区间覆盖 + 逻辑推导的组合计数问题,关键在于利用区间包含关系和品种分布的位置信息直接判定。
-
算法选择:
- 模拟 + 贪心推导:不需要遍历所有牛对,而是利用品种分布的极值位置(首尾出现位置)简化条件
- 分类讨论思想:将牛的品种分为“首品种 A”和“另一种 B”,由于领导者的定义具有传递性,可推导出满足条件的候选牛集非常有限,从而直接计数
- 区间包含判定:用 \(E_i\) 与品种首尾位置比较,快速判断列表是否覆盖全部同品种牛
-
关键步骤(基于课堂笔记的精简最优解法):
- 预处理:读入 \(N\),品种字符串 \(s\)(调整为 1-based),\(E[1..N]\)
- 确定品种分布边界:
- 设 \(s[1]\) 的品种为 \(A\)(第一头牛的品种),另一品种为 \(B\)
- 找出 \(A\) 品种的最后一头位置 \(lastA\)(遍历取最后出现的 \(A\))
- 找出 \(B\) 品种的第一头位置 \(firstB\)(遍历找到第一个非 \(A\) 的位置)
- 推导候选领导者范围:根据题意,只有 \(A\) 品种中位置在 \([1, firstB)\) 的牛(即 \(B\) 品种第一头之前的 \(A\) 牛)才可能成为领导者,因为若 \(A\) 牛位置 \(i \ge firstB\),其区间起点已在 \(B\) 品种中,无法覆盖 \(A\) 品种的第一头牛(位置 \(1\)),故不可能包含所有 \(A\) 牛
- 计数 \(A\) 品种的领导者数量(遍历 \(i\) 从 \(1\) 到 \(firstB-1\)):
- 若 \(i == 1\) 且 \(E[1] \ge lastA\)(第一头 \(A\) 牛覆盖所有 \(A\) 牛),则该牛是领导者
- 若 \(E[i] \ge firstB\)(该 \(A\) 牛的列表覆盖到 \(B\) 品种第一头,即覆盖了 \(B\) 的领导者,因为 \(B\) 的领导者必然在 \(B\) 品种范围内且位置 \(\ge firstB\)),则该牛也是领导者
- 两种情况满足任一即计数加 \(1\)
- 统计答案:上述计数即为全部可能的领导者对数(因为 \(B\) 品种的领导者唯一且必为 \(B\) 品种第一头牛,且 \(A\) 的每个领导者与 \(B\) 的唯一领导者均可组成一对),直接输出计数
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),仅需若干次线性扫描
- 空间复杂度:\(O(N)\),存储 \(E\) 数组和品种字符串
-
模拟 + 分类讨论的核心思想:
- 位置边界简化:利用“品种首尾位置”作为关键边界,将区间覆盖判定从 \(O(N^2)\) 降为 \(O(1)\)
- 领导者唯一性:在每种品种中,能覆盖该品种所有牛的领导者最多一头(必须是该品种最靠前的牛且 \(E\) 够大),这大幅缩小了候选集合
- 包含关系的非对称性:通过将品种分为 \(A\) 和 \(B\),并只枚举 \(A\) 种的候选者,利用对称逻辑避免重复计数
- 隐含条件挖掘:只有位于另一种品种第一头之前的牛,才可能覆盖自身品种的全部牛,这是关键的剪枝条件
- 适用于具有“区间覆盖”和“类别包含”约束的配对计数问题,核心是找出决定性的少数关键位置
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, e[200005], firstH=0, lastH=0, firstG=0, lastG=0, ansG[200005]={0}, ansH[200005]={0}, cntG=0, cntH=0; // ans1记录该头牛是否包含所有牛
int leaderG=0, leaderH=0;
string s;
int main()
{
cin >> n; // 输入n
cin >> s; // 输入字符串(方便查找)
s = " " + s; // 因为起始位置为1,所以在字符串前面加个空格
for (int i=1; i<=n; i++) { // 遍历n头奶牛
cin >> e[i]; // 记录每头奶牛的名单
}
firstG = s.find('G'); // 获取第一头G奶牛的位置
lastG = s.rfind('G'); // 获取最后一头G奶牛的位置
firstH = s.find('H'); // 获取第一头H奶牛的位置
lastH = s.rfind('H'); // 获取最后一头H奶牛的位置
for (int i=1; i<=n; i++) { // 遍历所有奶牛,找到包含所有同种品种奶牛的位置
if (s[i]=='G') { // 找G牛
if (i<=firstG && e[i]>=lastG) { // 如果包含所有同种品种的奶牛
ansG[i]=1; // 标记为1
leaderG = i; // 并且记录位置(G中有且只有一头头牛)
}
}
if (s[i]=='H') { // 找H牛
if (i<=firstH && e[i]>=lastH) { // 如果包含所有同品种的奶牛
ansH[i]=1; // 标记为1
leaderH = i; // 并记录位置(H中也有且只有一头头牛)
}
}
}
for (int i=1; i<=n; i++) { // 再次遍历n头奶牛
if (s[i]=='G') { // 查找满足条件:名单上记录另一品种奶牛的领导者
if (i<=leaderH && e[i]>=leaderH) ansG[i]= 1; // 满足条件的标记为1
}
if (s[i]=='H') { // 同上
if (i<=leaderG && e[i]>=leaderG) ansH[i]= 1;
}
}
for (int i=1; i<=n; i++) { // 最后将G牛和H牛种中标记为1的牛进行统计
if (ansG[i]==1) cntG++;
if (ansH[i]==1) cntH++;
}
cout << cntG*cntH << endl; // 将两者统计结果相乘,得到方案数
return 0;
}
// 学而思课堂笔记
#include <bits/stdc++.h>
using namespace std;
int n, a[100005], ans;
//第i头奶牛的领导范围:i~a[i];
string s;
int main()
{
cin >> n >> s;
s = " " + s;
for (int i=1; i<=n; i++) cin >> a[i];
int Alast, Bfirst; // 我们将s[1]的奶牛的品种称为A,另一种称为B
// A种奶牛的最后一头编号为Alast,B种奶牛的第一头编号为Bfirst
for (int i=1; i<=n; i++) { // 找最后一头A种奶牛的编号
if (s[i]==s[1]) {
Alast = i;
}
}
for (int i=2; i<=n; i++) { // 找第一头B种奶牛的编号
if (s[i]!=s[1]) {
Bfirst = i;
break;
}
}
// A种奶牛成为领导者的条件:
// 1.领导所有A种奶牛:i==1 && a[i]>=Alast
// 2.领导B种奶牛领导者:a[i]>=Bfirst
for (int i=1; i< Bfirst; i++) {
if (i==1 && a[i]>=Alast || a[i]>=Bfirst) {
ans++;
}
}
cout << ans << endl;
return 0;
}
【运行结果】
4
GHHG
2 4 3 4
1
浙公网安备 33010602011771号