题目传送门
题意
给你一个长度为 \(N\) 序列 \(A\),保证每一个数 \(A_i \le C\),要求对于从 \(1\) 到 \(C\) 中的每一个数都在序列中寻找闭区间,使得区间中至少有一个数等于它,输出从 \(1\) 到 \(C\) 中的每一个数的满足条件的所有闭区间。
解题思路
看到这道题,不知道各位有没有跟我一样想到排列组合,因为暴搜肯定超时。
首先,当你想到排列组合时,你已经成功了三分之一。
其次,可以想到,将所有相同的数的下标放到一个数组 \(v\) 里,然后假设当前要搜索的数字为 \(X\),那么对于在数组 \(v\) 中每一个与 \(X\) 相等的数 \(Y\),让左端点 \(l\) 小于等于当前 \(Y\) 的下标 \(i\),右端点 \(r\) 大于等于当前 \(Y\) 的下标 \(i\),即保证当前的 \(A_i = X\) 且位于区间中,一共有 \(i \times (n-i)\) 个区间满足我们的条件。
哦,你问我为什么是大于等于与小于等于,因为我们选的是闭区间,也就是可以选 \([i,i]\),自然是大于等于与小于等于了。
如果看不懂文字描述,可以结合看看下面这幅图。

紧接着,你会发现,你找出的区间数大于实际区间数。这是因为有重复的区间被选择,如下图,以图片左端绿色区间为所选区间左端点,右端绿色区间为所选区间右端点的区间被重复选择。

那么如何去重就是我们要解决的下一个问题。
于是,我想到我们不能对于所有左端点 \(l \le i\) 都纳入答案,但是对于所有右端点 \(i \le r\) 是可以被都选择的。所以我们的左端点只需在当前数组位置到前一个与 \(X\) 值相等的数的数组位置的区间中寻找。
注意,左端点不与前一个与 \(X\) 值相等的数的数组位置相等,即设前一个与 \(X\) 值相等的数的数组位置为 \(i\),则 \(i < l\)。
看不懂文字可以结合下图理解。

这样选择就可以避免重复选择区间。
注:以上所有图片中的不同颜色的箭头以及黑色竖线代表值相同但下标不同的数组中的数。
代码实现
如果前面思路结合图片还看不懂,可以理解一下代码。
本题代码如下:
#include<bits/stdc++.h>
using namespace std;
unsigned long long n,a[100005],c;
vector<unsigned long long>v[100005];
int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>c;for(int i=1;i<=n;++i){cin>>a[i];v[a[i]].push_back(i);}for(int i=1;i<=c;++i){unsigned long long ans=0;for(int j=0;j<v[i].size();++j){unsigned long long u=v[i][j];if(j!=0)ans+=(unsigned long long )((n-u+1)*(u-v[i][j-1]));else ans+=(unsigned long long )((n-u+1)*u);} cout<<ans<<"\n";}return 0;
}
警钟长鸣
本题需开 long long, 当然,本人习惯开了 unsigned long long。
注意特判数组中第一个与 \(X\) 值相同的数。
注意数组大小。
最后感谢您的留步与观看,希望本篇题解能够帮到您。