既然是让我两天都想不明白的题,就写总结吧。
题面
题意:给你一个n个数的随机排列,设ans[k]为区间内(最大值-最小值)≤k的区间个数。
求ans[0~n-1]。
暴力就是固定左端点,右端点不断向右拓展,用得到的新数来更新当前最大值与最小值。
实际操作中,我们发现很多次拓展都不能更新最大值与最小值。
就萌发出一个玄学优化:每次直接跳到能更新最大值或最小值的点,然后统计答案。
这用两个单调栈即可预处理出每个点右边第一个比它大(小)的数。
然后据说由于这是一个随机排列,有证明这样就是O(nlogn)的。
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))
typedef long long LL;
const int N=100100;
int T,n,a[N];
int big[N],small[N];
int st1[N],l1,st2[N],l2;
long long ans[N];
int main()
{
cin>>T;
while(T--)
{
l1=l2=0;
mmst(ans,0);
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
st1[0]=st2[0]=n+1;
for(int i=n;i>=1;i--)
{
while(l1&&a[st1[l1]]<=a[i])
l1--;
while(l2&&a[st2[l2]]>=a[i])
l2--;
big[i]=st1[l1];
small[i]=st2[l2];
st1[++l1]=i;
st2[++l2]=i;
}
for(int i=1;i<=n;i++)
{
int p=i,s=i,b=i;
while(p<=n)
{
int ne;
if(small[s]<big[b])
{
ne=small[s];
ans[a[b]-a[s]]+=ne-p;
p=s=ne;
}
else
{
ne=big[b];
ans[a[b]-a[s]]+=ne-p;
p=b=ne;
}
}
}
cout<<n<<endl;
ans[0]=n;
for(int i=1;i<n;i++)
{
ans[i]+=ans[i-1];
printf("%lld\n",ans[i]);
}
}
return 0;
}