题面:
一个长度为 n 的序列,对于每个位置 i 的数 ai都有一个优美值,其定义是:找到序列中最长的一段 [l, r],满足 l ≤ i ≤ r,且 [l, r] 中位数为 ai (我们比较序列中两个位置的数的大小时,以数值为第一关键字,下标为第二关键字比较。这样的话 [l, r] 的长度只有可能是奇数),r - l+ 1 就是 i 的优美值。
接下来有 Q 个询问,每个询问 [l, r] 表示查询区间 [l, r] 内优美值的最大值。
【输入】
第一行输入整数n ,第二行输入n 个整数ai ,第三行输入整数 Q,代表有 Q 个区间,接下来 Q 行,每行两个整数 l, r(l ≤ r),表示区间的左右端点。
【输出】
对于每个区间的询问,输出答案
【数据范围】
对于 30% 的数据,满足 n,Q ≤ 50
对于 70% 的数据,满足 n,Q ≤ 2000
对于所有数据,满足 n ≤ 2000, Q ≤ 1000000,ai ≤ 1000000
首先我们会想到一个十分简单的但非正解做法:n^nlogn+一大堆常数通过对顶堆来求出每个位置的最大值;然后ST表nlogn处理就好了;
然后细细挖掘这道题,对于每个位置,把大于他的值变成1,否则变成0,然后求一段最大的区间使得区间总和是0,且包含该点;
这样n^nlogn+nlogn的算法就优化成了n^n+nlogn;
下面给出对顶堆的代码:
#include <bits/stdc++.h>
#define inc(i,a,b) for(register int i=a;i<=b;i++)
using namespace std;
template<class nT>
inline void read(nT& x)
{
char c;while(c=getchar(),!isdigit(c));
x=c^48;while(c=getchar(),isdigit(c)) x=x*10+c-48;
}
const int MXR=1e6+1e5;
int a[MXR];
priority_queue<pair<int,int> > q1,q2;//q1是大根堆,q2是小根堆
int ans[MXR];
int f[MXR][25];
int n;
int pre[MXR];
void build()
{
inc(i,1,n) f[i][0]=ans[i];
inc(j,1,20){
for(int i=1;i+(1<<j)-1<=n;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
inc(j,0,19){
inc(i,(1<<j),(1<<(j+1))-1){
if(i>MXR) break;
pre[i]=j;
}
}
}
int main()
{
read(n);
inc(i,1,n) read(a[i]);
inc(i,1,n){
while(q1.size()) q1.pop();
while(q2.size()) q2.pop();
inc(j,i,n){
if(q1.size()&&a[j]>q1.top().first) q2.push(make_pair(-a[j],j));
else q1.push(make_pair(a[j],j));
if((j-i+1)%2==0){
continue;
}
while(q2.size()>(j-i+1)/2){
q1.push(make_pair(-q2.top().first,q2.top().second));
q2.pop();
}
while(q2.size()<(j-i+1)/2){
q2.push(make_pair(-q1.top().first,q1.top().second));
q1.pop();
}
ans[q1.top().second]=max(ans[q1.top().second],(j-i+1));
}
}
build();
int q; read(q);
inc(i,1,q){
int x,y; read(x); read(y);
int logg=pre[y-x+1];
int anss=max(f[x][logg],f[y-(1<<logg)+1][logg]);
printf("%d
",anss);
}
}
/*
8
16 19 7 8 9 11 20 16
8
3 8
1 4
2 3
1 1
5 5
1 2
2 8
7 8
*/