【USACO25OPEN】It‘s Mooin‘ Time III B
【USACO25OPEN】It’s Mooin’ Time III B
USACO 专栏:USACO 2025 OPEN
算法竞赛:二分,二分查找,枚举
题目链接:洛谷 P12024 [USACO25OPEN] It’s Mooin’ Time III B
题目描述:
Elsie 正在试图向 Bessie 描述她最喜欢的 USACO 竞赛,但 Bessie 很难理解为什么 Elsie 这么喜欢它。Elsie 说「现在是哞哞时间!谁想哞哞?请,我只想参加 USACO」。
Bessie 仍然不理解,于是她将 Elsie 的描述转文字得到了一个长为 NNN(3≤N≤1053 \leq N \leq 10^53≤N≤105)的字符串,包含小写字母字符 s1s2…sNs_1s_2 \ldots s_Ns1s2…sN。Elsie 认为一个包含三个字符的字符串 ttt 是一个哞叫,如果 t2=t3t_2 = t_3t2=t3 且 t2≠t1t_2 \neq t_1t2=t1。
一个三元组 (i,j,k)(i, j, k)(i,j,k) 是合法的,如果 i<j<ki < j < ki<j<k 且字符串 sisjsks_i s_j s_ksisjsk 组成一个哞叫。对于该三元组,FJ 执行以下操作计算其值:
- FJ 将字符串 sss 在索引 jjj 处弯曲 90 度
- 该三元组的值是 Δijk\Delta ijkΔijk 的面积的两倍。
换句话说,该三元组的值等于 (j−i)(k−j)(j-i)(k-j)(j−i)(k−j)。
Bessie 向你进行 QQQ(1≤Q≤3⋅1041 \leq Q \leq 3 \cdot 10^41≤Q≤3⋅104)个查询。在每个查询中,她给你两个整数 lll 和 rrr(1≤l≤r≤N1 \leq l \leq r \leq N1≤l≤r≤N,r−l+1≥3r-l+1 \ge 3r−l+1≥3),并询问满足 l≤il \leq il≤i 和 k≤rk \leq rk≤r 的所有合法三元组 (i,j,k)(i, j, k)(i,j,k) 的最大值。如果不存在合法的三元组,输出 −1-1−1。
注意这个问题涉及到的整数可能需要使用 64 位整数类型(例如,C/C++ 中的long long)。
输入格式:
输入的第一行包含两个整数 NNN 和 QQQ。
以下一行包含 s1s2,…sNs_1 s_2, \ldots s_Ns1s2,…sN。
以下 QQQ 行每行包含两个整数 lll 和 rrr,表示每个查询。
输出格式:
对于每一个查询输出一行,包含对于该查询的答案。
奶牛时间(三) It’s Mooin’ Time III B
题目大意
一个包含三个字符的字符串 t[t1,t2,t3]t[t_1,t_2,t_3]t[t1,t2,t3],如果 t2=t3t_2 = t_3t2=t3 且 t2≠t1t_2 \neq t_1t2=t1,则字符串 ttt 是一个哞叫。
一个三元组 (i,j,k)(i, j, k)(i,j,k),如果 i<j<ki < j < ki<j<k 且字符串 sisjsks_i s_j s_ksisjsk 组成一个哞叫,则这个三元组是合法的,该三元组的值等于 (j−i)(k−j)(j-i)(k-j)(j−i)(k−j)。
输入一个长为 NNN(3≤N≤1053 \leq N \leq 10^53≤N≤105)的字符串,包含小写字母字符 s1s2…sNs_1s_2 \ldots s_Ns1s2…sN。
接下来会有 QQQ(1≤Q≤3×1041 \leq Q \leq 3 \times 10^41≤Q≤3×104)个查询。在每个查询中,给出两个整数 lll 和 rrr(1≤l≤r≤N1 \leq l \leq r \leq N1≤l≤r≤N,r−l+1≥3r-l+1 \ge 3r−l+1≥3),并询问满足 l≤il \leq il≤i 和 k≤rk \leq rk≤r 的所有合法三元组 (i,j,k)(i, j, k)(i,j,k) 的最大值。如果不存在合法的三元组,输出 −1-1−1。
题目分析
这道题我们采用贪心策略和二分优化,在区间 l∼rl\sim rl∼r 中,我们枚举这个区间中所出现过的字符 ccc,找两个位置 i,ji,ji,j,使 si=sj=cs_i=s_j=csi=sj=c,则 si,sjs_i,s_jsi,sj 就充当 t2,t3t_2,t_3t2,t3,对于 t2,t3t_2,t_3t2,t3,我们可以确定 t3t_3t3 应为区间 l∼rl\sim rl∼r 中最后一个字符 ccc,而 t2t_2t2 应从区间 l∼rl\sim rl∼r 第一个字符 ccc 枚举到最后一个字符 ccc 之前,在此过程中,t1t_1t1 一直不变,即为区间 l∼rl\sim rl∼r 中第一个不为 ccc 的字符,每次取最大值更新答案。
解法步骤
- 将给出的字符串中出现的字符分类,统计每种字符出现的位置,在每次询问中,执行以下操作:
- 给出区间 l∼rl\sim rl∼r,遍历字符串中出现的每种字符 ccc(即使在区间 l∼rl\sim rl∼r 中没有出现过也无妨);
- 找到区间 l∼rl\sim rl∼r 中第一个与 ccc 不相同的字符的位置,记录为 site1site_1site1(枚举即可);
- 找到区间 l∼rl\sim rl∼r 中最后一个 ccc 字符,记录其位置为 site3site_3site3,找到区间 l∼rl\sim rl∼r 中第一个 ccc 字符,记录其指针为 ititit,指针 ititit 向后枚举(字符 ccc 出现的位置);
- ititit 所指的位置即为 site2site_2site2,本次得到的三元组的值为 num=(site3−site2)×(site2−site1)num=(site_3-site_2)\times (site_2-site_1)num=(site3−site2)×(site2−site1),更新结果 ans=max(ans,num)ans=\max(ans,num)ans=max(ans,num)。
代码指导
- 可以使用 vector 容器储存每种字符 ccc 出现的位置,以方便二分查找。
- 可以使用语句
auto it=upper_bound(G[id].begin(),G[id].end(),l);来找到区间中字符 ccc 第一次出现的位置。 - 可以使用语句
auto itt=upper_bound(G[id].begin(),G[id].end(),r);itt--;来找到区间中字符 ccc 最后一次出现的位置。 - 在 t2t_2t2 位置枚举过程中,可以添加语句
if ((long long)d*_d<=num) break;,表示如果当前的 (site3−site2)×(site2−site1)(site_3-site_2)\times (site_2-site_1)(site3−site2)×(site2−site1) 小于或等于已有的 numnumnum 就可以不再继续枚举,因为 t2t_2t2 位置越靠中间三元组的值越大。
AC Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
char s[N];
vector<int>G[N],K;
map<char,int>mp;
int main()
{int n,q,id=0;scanf("%d%d",&n,&q);scanf("%s",s+1);for (int i=1; i<=n; i++){if (!mp[s[i]]) mp[s[i]]=++id;G[mp[s[i]]].push_back(i);}for (int i=1; i<=n; i++) K.push_back(mp[s[i]]);sort(K.begin(),K.end());K.erase(unique(K.begin(),K.end()),K.end());while (q--){int l,r;long long ans=-1;scanf("%d%d",&l,&r);for (int id : K){long long num=-1;auto it=upper_bound(G[id].begin(),G[id].end(),l);auto itt=upper_bound(G[id].begin(),G[id].end(),r);itt--;int e=l;while (mp[s[e]]==id) e++;while (it!=itt&&it!=G[id].end()&&*it<=e) it++;while (it!=itt&&it!=G[id].end()){int d=*it-e;int _d=*itt-*it;if ((long long)d*_d<=num) break;num=max(num,(long long)d*_d);it++;}ans=max(ans,num);}printf("%lld\n",ans);}return 0;
}
时间复杂度
有 qqq 次询问,枚举字符 ccc 有 C=26\text{C}=26C=26 种,t2t_2t2 枚举的次数为 nnn,时间复杂度为 O(C⋅qn)\mathcal{O(\text{C}\cdot qn)}O(C⋅qn)。
博客说明
本 CSDN 博客最早发布于洛谷文章广场,作者:ZZA000HAH,与 2025/11/16 同步至 CSDN 博客平台。
