一个有趣的期望问题:1~n随机排列前缀最大值集合元素个数的期望
解法
- 元素
x
对答案如果产生了贡献,那么比x大的数字都在x的后面。 - x成为天选之子,概率为
1/(n-x+1)
- 独立考虑每个元素对答案的贡献,所以答案为
sigma 1/i
,其中i: 1->n
做法: Meow
做法:
设f(L,R)
表示max(a[L~R])-min(a[L,R])
- 逐个枚举左端点
- 逐个枚举右端点
然后,我们发现逐个枚举右端点很辣鸡的。
我们用单调栈,维护在元素i
后面的第一个大于a[i]
的元素,与第一个小于a[i]
的元素。
确定L,我们就可以把f(L,R)
相等的R
一起处理了。
复杂度O(NlogN)
,因为对1/x
积分可知:1/1+1/2+...+1/n ~ log N
code
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int N = 100000+10;
pair<int,int> stk[N]; int top;
int T, n, a[N], nex1[N], nex2[N];
long long cnt[N];
void brute() {
long long sum[N]={0};
for(int i=1;i<=n;i++){
int mx=-1,mn=N;
for(int j=i;j<=n;j++){
mx=max(mx,a[j]); mn=min(mn,a[j]);
sum[mx-mn]++;
if(mx-mn==2) printf("[%d,%d]\n", i,j);
}
}
for(int i=1;i<=n;i++){
sum[i]+=sum[i-1];
}
for(int i=0;i<n;i++) {
printf("%lld\n", sum[i]);
}
}
int main() {
scanf("%d",&T);
while (T --){
memset(cnt,0,sizeof(cnt));
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
//brute();
top=0; stk[++top]=make_pair(N, n+1);
for(int i=n;i>=1;i--) {
while(a[i]>stk[top].first) top--;
nex1[i]=stk[top].second;
stk[++top]=make_pair(a[i],i);
}
top=0; stk[++top]=make_pair(0, n+1);
for(int i=n;i>=1;i--) {
while(a[i]<stk[top].first) top--;
nex2[i]=stk[top].second;
stk[++top]=make_pair(a[i],i);
}
vector<int> v;
for(int i=1;i<=n;i++){
v.clear();
int now=i;
while(1) {
now=nex1[now];
if(now==n+1) break;
v.push_back(now);
}
now=i;
while(1) {
now=nex2[now];
if(now==n+1) break;
v.push_back(now);
}
v.push_back(n);
sort(v.begin(),v.end());
int mx=a[i],mn=a[i],pre=i;
for(int j=0;j<v.size();j++){
cnt[mx-mn]+=v[j]-pre;
mx=max(mx,a[v[j]]), mn=min(mn,a[v[j]]);
pre=v[j];
}
cnt[mx-mn]++;
}
for(int i=0;i<n;i++) {
printf("%lld\n", cnt[i]);
cnt[i+1]+=cnt[i];
}
}
}