| 破壁人五号 低级 OIer | 成都市第三区计算机学会提醒您 条件千万条,边界第一条 边界判不对,出错两行泪 {\tiny\text{\colorbox{grey}|破壁人五号 低级 OIer\colorbox{grey}|}}\\{\small\text{成都市第三区计算机学会提醒您}}\\ \large\text{条件千万条,边界第一条}\\ \text{边界判不对,出错两行泪} |破壁人五号 低级 OIer|成都市第三区计算机学会提醒您条件千万条,边界第一条边界判不对,出错两行泪
题意
有一个宽为 n n n 的直方图,每个条形的高度为 h i h_i hi。问所有(共 n ( n − 1 ) 2 n(n-1)\over 2 2n(n−1) 个)极高子矩形中第 L L L 到 R R R 大的的面积。
题解
下文可能会直接用一个区间 [ l , r ] [l,r] [l,r] 代表左、右延伸到 [ l , r ] [l,r] [l,r] 的极高矩形。
首先单调栈(或者别的什么办法)求出 i i i 两侧最近的、高度不及 i i i 的条形的位置,记为 L i , R i L_i,R_i Li,Ri(注意处理高度相等的情况,最方便的办法是一边取小于、一边取小于等于,即维护求 L i , R i L_i,R_i Li,Ri 用的单调栈时一个取等号、一个不取),显然对于 [ l ′ , r ′ ] ∈ ( L i , R i ) 且 [ l ′ , r ′ ] ∩ i ≠ ∅ [l',r']\in(L_i,R_i) \text{ 且 }[l',r']\cap i\neq \varnothing [l′,r′]∈(Li,Ri) 且 [l′,r′]∩i=∅,矩形 [ l ′ , r ′ ] [l',r'] [l′,r′] 的高度为 h i h_i hi。(之后对于 h h h 大小关系的判断都基于 L i , R i L_i,R_i Li,Ri,可以假装每个条形的高度不同。)
首先二分第 L L L 大的矩形会有多大,check 时统计面积小于 m i d mid mid 的矩形的数量,即枚举 i i i,考虑高度为 h i h_i hi(横跨过 i i i 且在 ( L i , R i ) (L_i,R_i) (Li,Ri) 内)的矩形中有多少宽度小于 m i d h i mid\over h_i himid 的,这个分类讨论一下可以 O ( 1 ) O(1) O(1) 算;因此每次 check 是 O ( n ) O(n) O(n) 的。
从面积小于第 L L L 大矩形的矩形数量开始枚举 r k rk rk 直到 R R R。接着维护一个堆,堆中每个元素对应一个条形 i i i,把元素 i i i 按照【高度为 h i h_i hi 且排名在 r k rk rk 以上的最小矩形】(可以维护这个矩形的 l , r l,r l,r)的面积排序。每次取出堆顶,把堆顶对应的面积作为下次的答案,并尝试把 i i i 对应的矩形加到下一个,如果能加到下一个,则把 i i i 及其对应的面积 push 回堆。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lli long long
#define pii pair<lli,int>
#define fi first
#define se second
#define mp make_pair
lli getint(){
lli ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=3e5+10;
int a[N];
lli n,l,r;
priority_queue<pii >q;
int le[N],ri[N];//the position of the nearest number that is smaller than it
int ll[N],rr[N];//the recent retangle of which the lowest part is i
void init_le(){
stack<int>sta;
sta.push(0);
for(int i=1;i<=n;i++){
while(a[sta.top()]>a[i])sta.pop();
le[i]=sta.top();
sta.push(i);
}
}
void init_ri(){
stack<int>sta;
sta.push(n+1);
for(int i=n;i;i--){
while(a[sta.top()]>=a[i])sta.pop();
ri[i]=sta.top();
sta.push(i);
}
}
lli calc(int i,lli len){
if(len>=ri[i]-le[i]-1){
ll[i]=le[i]+1;
rr[i]=ri[i]-1;
return (i-le[i])*1ll*(ri[i]-i);
}
lli p=i-le[i],q=ri[i]-i;
lli ans=len*(len+1ll)/2;
if(len-p>=0)ans-=(len-p)*(len-p+1ll)/2;
if(len-q>=0)ans-=(len-q)*(len-q+1ll)/2;
return ans;
}
lli check(lli mid){
lli ans=0;
for(int i=1;i<=n;i++){
lli len=(mid-1)/a[i];
ans+=calc(i,len);
len=min(len,(long long)ri[i]-le[i]-1);
if(ri[i]-len+1<=i)ll[i]=ri[i]-len,rr[i]=ri[i]-1;
else ll[i]=i,rr[i]=i+len-1;
}
return ans;
}
lli nxt(int i){
if(rr[i]==ri[i]-1||ll[i]==i)return a[i]*(rr[i]-ll[i]+2ll);
else a[i]*(rr[i]-ll[i]+1ll);
}
bool pop(int i){
if(ll[i]==le[i]+1&&rr[i]==ri[i]-1)return 0;
if(rr[i]==ri[i]-1||ll[i]==i){
if(le[i]+rr[i]-ll[i]+2<i) ll[i]=i-rr[i]+ll[i]-1,rr[i]=i;
else rr[i]=le[i]+rr[i]-ll[i]+2,ll[i]=le[i]+1;
}
else ll[i]++,rr[i]++;
return 1;
}
signed main(){
freopen("rectangle.in","r",stdin);
freopen("rectangle.out","w",stdout);
n=getint();
bool is_subtask_1=1,is_subtask_i=1;
for(int i=1;i<=n;i++){
a[i]=getint();
if(a[i]!=1)is_subtask_1=0;
if(a[i]!=i)is_subtask_i=0;
}
l=getint(),r=getint();
init_le();
init_ri();
for(int i=1;i<=n;i++){
ll[i]=ri[i],rr[i]=ri[i]-1;
}
lli ans=0;
{
lli p=1,q=0x7f7f7f7f7f7f7fll,mid=0;
while(p<=q){
mid=(p+q)>>1;
if(check(mid)<l)ans=mid,p=mid+1;
else q=mid-1;
}
assert(ans);
}
int qaq=check(ans);
for(int i=1;i<=n;i++){
if(pop(i))q.push(mp(-(rr[i]-ll[i]+1ll)*a[i],i));
}
for(int i=qaq;i<=r;i++){
if(i>=l)printf("%lld ",ans);
pii t=q.top();q.pop();
ans=-t.fi;
if(pop(t.se))q.push(mp(-(rr[t.se]-ll[t.se]+1ll)*a[t.se],t.se));
}
return 0;
}