题面:
一个长度为 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 */