AcWing 6135. 奶牛体检
农夫约翰的 N 头奶牛站成一行,奶牛 1 在队伍的最前面,奶牛 N在队伍的最后面。
农夫约翰的奶牛也有许多不同的品种。
他用从 1 到 N 的整数来表示每一品种。
队伍从前到后第 i 头奶牛的品种是 ai。
农夫约翰正在带他的奶牛们去当地的奶牛医院进行体检。
然而,奶牛兽医非常挑剔,仅愿意当队伍中第 i 头奶牛为品种 bi 时对其进行体检。
农夫约翰很懒惰,不想完全重新排列他的奶牛。
他将执行以下操作恰好一次。
选择两个整数 l 和 r,使得 1≤l≤r≤N。反转队伍中第 l 头奶牛到第 r 头奶牛之间的奶牛的顺序。
农夫约翰想要衡量这种方法有多少效果。
对于每一个 c=0…N,请帮助农夫约翰求出使得恰好 c 头奶牛被检查的不同操作 (l,r)的数量。
两种操作 (l1,r1)
和 (l2,r2) 不同,如果 l1≠l2 或者 r1≠r2
输入格式
输入的第一行包含 N
第二行包含 a1,a2,…,aN
第三行包含 b1,b2,…,bN
输出格式
输出 N+1 行,第 i 行包含使得 i−1 头奶牛被检查的不同操作 (l,r)的数量。
数据范围
1≤N≤7500
1≤ai,bi≤N
(枚举)
暴力的想法,枚举所有的区间,记录下如果翻转该区间后会有多少个 a[i]==b[i] ,记为 sum
首先,要记录原序列下有多少个 a[i]==b[i] ,因为翻转区间是在原序列基础上进行,将为影响原序列 sum 的变化。
如果是常规的枚举左端点再枚举右端点时间复杂度已经是 O(n2),然后要处理每个区间翻转后影响,最后时间复杂度会是 O(n3) ,会超时不可行。
所以不要单纯枚举区间端点,而是从一个点开始向左右扩展,这时对于 [l,r] 我们已知 [l+1,r−1] 的 sum,就只需思考新扩展的两个整数 a[l]和a[r] 在翻转后的影响,直到区间的某个端点到了边界后结束。
思考a[l]和a[r] 在翻转后的影响:
如果 a[l]=b[l] ,翻转就要减去这条记录,即 sum–
如果 a[r]=b[r] ,翻转就要减去这条记录,即 sum–
如果 a[l]=b[r] ,翻转就要加上这条记录,即 sum++
如果 a[r]=b[l] ,翻转就要加上这条记录,即 sum++
特别的,对于奇数和偶数区间要分别枚举。
最后,每个区间翻转后的 sum 即是恰好有 sum 个 a[i]==b[i] 的一个答案,用一个数组来记录每个 sum 有多少个,就是答案。
时间复杂度 O(n2)
java
import java.util.Scanner;
public class Main{
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
//sc.nextLine(); // 清除残留的换行符
int []a=new int[7510];
int []b=new int[7510];
for(int i=0;i<n;i++)
a[i]=sc.nextInt();
for(int i=0;i<n;i++)
b[i]=sc.nextInt();
int cnt=0;
for(int i=0;i<n;i++){
if(a[i]==b[i])
cnt++;
}
int []ans=new int[7510];
for(int i=0;i<n;i++){
for(int j=0;j<2;j++){
int sum=cnt;
for(int l=i,r=i+j;l>=0&&r<n;l--,r++){
if(a[l]==b[l])
sum--;
if(a[r]==b[r])
sum--;
if(a[l]==b[r])
sum++;
if(a[r]==b[l])
sum++;
ans[sum]++;
}
}
}
for(int i=0;i<=n;i++){
System.out.println(ans[i]);
}
}
}
不需要sc.nextLine()代码能正确运行的原因
输入格式的巧合性
当输入的n和数组数据按照以下格式排列时:
3
1 3 2
3 2 1
n=3后的换行符会被后续的nextInt()自动跳过。
数组a和b的数据位于独立行,nextInt()会直接跳过换行符并读取下一行的数值。
nextInt()的特性
nextInt()在读取数字时,会忽略所有前导的空白字符(包括换行符),因此即使不调用sc.nextLine(),也能正确读取后续行的数据。
为什么不需要sc.nextLine()?
测试用例的特殊性
题目中的输入数据严格按以下格式排列:
第一行是n
第二行是数组a的n个数字
第三行是数组b的n个数字
这种情况下,nextInt()会按预期跳过换行符,直接读取后续行的数值。
潜在风险
如果输入数据格式不符合预期(例如,n和数组a在同一行),或者存在多余的空格/换行符,代码可能抛出NoSuchElementException。例如:
3 1 2 3 4 5 6
这种输入会导致n=3后的数字被错误地读入数组a和b,最终导致逻辑错误。
安全做法
为了代码的健壮性,建议始终在nextInt()后调用sc.nextLine():
int n = sc.nextInt();
sc.nextLine(); // 清除残留的换行符
这样能确保输入流的正确对齐,避免因输入格式变化导致的意外错误。
总结
你的代码能正确运行是因为测试用例的输入格式恰好符合nextInt()的自动跳转特性。虽然在此特定场景下不需要处理换行符,但为了代码的通用性和可靠性,显式清除换行符是更安全的做法。
c++