【USACO25OPEN】It‘s Mooin‘ Time III B

USACO Mooing Time III 问题解析

【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 的描述转文字得到了一个长为 N N N 3 ≤ N ≤ 1 0 5 3 \leq N \leq 10^5 3N105)的字符串,包含小写字母字符 s 1 s 2 … s N s_1s_2 \ldots s_N s1s2sN。Elsie 认为一个包含三个字符的字符串 t t t 是一个哞叫,如果 t 2 = t 3 t_2 = t_3 t2=t3 t 2 ≠ t 1 t_2 \neq t_1 t2=t1
一个三元组 ( i , j , k ) (i, j, k) (i,j,k) 是合法的,如果 i < j < k i < j < k i<j<k 且字符串 s i s j s k s_i s_j s_k sisjsk 组成一个哞叫。对于该三元组,FJ 执行以下操作计算其值:

  • FJ 将字符串 s s s 在索引 j j j 处弯曲 90 度
  • 该三元组的值是 Δ i j k \Delta ijk Δijk 的面积的两倍。

换句话说,该三元组的值等于 ( j − i ) ( k − j ) (j-i)(k-j) (ji)(kj)
Bessie 向你进行 Q Q Q 1 ≤ Q ≤ 3 ⋅ 1 0 4 1 \leq Q \leq 3 \cdot 10^4 1Q3104)个查询。在每个查询中,她给你两个整数 l l l r r r 1 ≤ l ≤ r ≤ N 1 \leq l \leq r \leq N 1lrN r − l + 1 ≥ 3 r-l+1 \ge 3 rl+13),并询问满足 l ≤ i l \leq i li k ≤ r k \leq r kr 的所有合法三元组 ( i , j , k ) (i, j, k) (i,j,k) 的最大值。如果不存在合法的三元组,输出 − 1 -1 1
注意这个问题涉及到的整数可能需要使用 64 位整数类型(例如,C/C++ 中的 long long)。

输入格式:
输入的第一行包含两个整数 N N N Q Q Q
以下一行包含 s 1 s 2 , … s N s_1 s_2, \ldots s_N s1s2,sN
以下 Q Q Q 行每行包含两个整数 l l l r r r,表示每个查询。
输出格式:
对于每一个查询输出一行,包含对于该查询的答案。


奶牛时间(三) It’s Mooin’ Time III B


题目大意

一个包含三个字符的字符串 t [ t 1 , t 2 , t 3 ] t[t_1,t_2,t_3] t[t1,t2,t3],如果 t 2 = t 3 t_2 = t_3 t2=t3 t 2 ≠ t 1 t_2 \neq t_1 t2=t1,则字符串 t t t 是一个哞叫。

一个三元组 ( i , j , k ) (i, j, k) (i,j,k),如果 i < j < k i < j < k i<j<k 且字符串 s i s j s k s_i s_j s_k sisjsk 组成一个哞叫,则这个三元组是合法的,该三元组的值等于 ( j − i ) ( k − j ) (j-i)(k-j) (ji)(kj)

输入一个长为 N N N 3 ≤ N ≤ 1 0 5 3 \leq N \leq 10^5 3N105)的字符串,包含小写字母字符 s 1 s 2 … s N s_1s_2 \ldots s_N s1s2sN

接下来会有 Q Q Q 1 ≤ Q ≤ 3 × 1 0 4 1 \leq Q \leq 3 \times 10^4 1Q3×104)个查询。在每个查询中,给出两个整数 l l l r r r 1 ≤ l ≤ r ≤ N 1 \leq l \leq r \leq N 1lrN r − l + 1 ≥ 3 r-l+1 \ge 3 rl+13),并询问满足 l ≤ i l \leq i li k ≤ r k \leq r kr 的所有合法三元组 ( i , j , k ) (i, j, k) (i,j,k) 的最大值。如果不存在合法的三元组,输出 − 1 -1 1


题目分析

这道题我们采用贪心策略和二分优化,在区间 l ∼ r l\sim r lr 中,我们枚举这个区间中所出现过的字符 c c c,找两个位置 i , j i,j i,j,使 s i = s j = c s_i=s_j=c si=sj=c,则 s i , s j s_i,s_j si,sj 就充当 t 2 , t 3 t_2,t_3 t2,t3,对于 t 2 , t 3 t_2,t_3 t2,t3,我们可以确定 t 3 t_3 t3 应为区间 l ∼ r l\sim r lr 中最后一个字符 c c c,而 t 2 t_2 t2 应从区间 l ∼ r l\sim r lr 第一个字符 c c c 枚举到最后一个字符 c c c 之前,在此过程中, t 1 t_1 t1 一直不变,即为区间 l ∼ r l\sim r lr 中第一个不为 c c c 的字符,每次取最大值更新答案。


解法步骤

  1. 将给出的字符串中出现的字符分类,统计每种字符出现的位置,在每次询问中,执行以下操作:
  2. 给出区间 l ∼ r l\sim r lr,遍历字符串中出现的每种字符 c c c(即使在区间 l ∼ r l\sim r lr 中没有出现过也无妨);
  3. 找到区间 l ∼ r l\sim r lr 中第一个与 c c c 不相同的字符的位置,记录为 s i t e 1 site_1 site1(枚举即可);
  4. 找到区间 l ∼ r l\sim r lr 中最后一个 c c c 字符,记录其位置为 s i t e 3 site_3 site3,找到区间 l ∼ r l\sim r lr 中第一个 c c c 字符,记录其指针为 i t it it,指针 i t it it 向后枚举(字符 c c c 出现的位置);
  5. i t it it 所指的位置即为 s i t e 2 site_2 site2,本次得到的三元组的值为 n u m = ( s i t e 3 − s i t e 2 ) × ( s i t e 2 − s i t e 1 ) num=(site_3-site_2)\times (site_2-site_1) num=(site3site2)×(site2site1),更新结果 a n s = max ⁡ ( a n s , n u m ) ans=\max(ans,num) ans=max(ans,num)

代码指导

  1. 可以使用 vector 容器储存每种字符 c c c 出现的位置,以方便二分查找。
  2. 可以使用语句 auto it=upper_bound(G[id].begin(),G[id].end(),l); 来找到区间中字符 c c c 第一次出现的位置。
  3. 可以使用语句 auto itt=upper_bound(G[id].begin(),G[id].end(),r);itt--; 来找到区间中字符 c c c 最后一次出现的位置。
  4. t 2 t_2 t2 位置枚举过程中,可以添加语句 if ((long long)d*_d<=num) break;,表示如果当前的 ( s i t e 3 − s i t e 2 ) × ( s i t e 2 − s i t e 1 ) (site_3-site_2)\times (site_2-site_1) (site3site2)×(site2site1) 小于或等于已有的 n u m num num 就可以不再继续枚举,因为 t 2 t_2 t2 位置越靠中间三元组的值越大。

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;
}

时间复杂度

q q q 次询问,枚举字符 c c c C = 26 \text{C}=26 C=26 种, t 2 t_2 t2 枚举的次数为 n n n,时间复杂度为 O ( C ⋅ q n ) \mathcal{O(\text{C}\cdot qn)} O(Cqn)

博客说明

本 优快云 博客最早发布于洛谷文章广场,作者:ZZA000HAH,与 2025/11/16 同步至 优快云 博客平台。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

HAH-HAH

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

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

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

打赏作者

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

抵扣说明:

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

余额充值