「ROI 2018 Day 1」Innophone (分块+斜率优化)
首先可以想到对于(x_i)排序,枚举(a)为某一个(x_i),那么可以将余下的部分分给(b)计算贡献
如果将(y_i)倒序排成(A_i,iin [1,k]),那么枚举(b)为某一个(A_i)时的贡献就是(icdot A_i)
同时要维护一个插入(A_i)的操作,可以固定(A_i),维护一个后缀个数加一的操作
(icdot A_i),同时维护(i ightarrow i+1)容易让人联想到斜率优化,而区间修改的斜率优化常见的可以用分块来维护
具体的,可以分为两个操作来维护:
1.下传所有(tag),同时块内建立凸包
2.块内权值(+1),同时在凸包上完成查询,打上tag
由于随着下标,权值存在单调性,所以构建凸包应该是比较简单的
同时查询时的斜率为整个块标记的值,查询有单调性,可以直接移动队列头完成
因此维护斜率优化是线性完成的,总复杂度为(O(nsqrt n))
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long double ldb;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int,int> Pii;
#define reg register
#define pb push_back
#define mp make_pair
#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,const T &b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,const T &b){ ((a<b)&&(a=b)); }
char IO;
template <class T=int> T rd(){
T s=0; int f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10,SN=600;
int n,len;
Pii A[N];
int h[N],hc;
int s[N];
struct Node{
int l,r,tag;
int Q[SN],L,R;
ll Val(int x) {
return 1ll*h[x]*(s[x]+tag);
}
ll Init() {
// 重新构建凸包
//插入是静态的,查询是单调的,可以线性维护
ll ans=0;
rep(i,l,r) cmax(ans,1ll*(s[i]+=tag)*h[i]);
tag=0,L=1,R=0;
drep(i,r,l) {
while(L<R && (Val(Q[R])-Val(Q[R-1]))*(h[i]-h[Q[R]]) <= (Val(i)-Val(Q[R]))*(h[Q[R]]-h[Q[R-1]])) R--;
Q[++R]=i;
}
return ans;
}
ll Add(){
tag++;
while(L<R && Val(Q[L+1])>=Val(Q[L])) L++;
return Val(Q[L]);
}
} S[SN];
ll ans,Ans;
void Add(int x) { // 向A_i中插入x,维护后缀+1操作
int p=x/len;
rep(i,x,min(hc,(p+1)*len-1)) s[i]++;
cmax(ans,S[p].Init());
rep(i,p+1,hc/len) cmax(ans,S[i].Add());
}
int main(){
n=rd();
rep(i,1,n) A[i].first=rd(),A[i].second=rd();
sort(A+1,A+n+1);
rep(i,1,n) h[++hc]=A[i].second;
sort(h+1,h+hc+1,greater<int>()),hc=unique(h+1,h+hc+1)-h-1;
rep(i,1,n) A[i].second=lower_bound(h+1,h+hc+1,A[i].second,greater<int>())-h;
len=sqrt(hc);
rep(i,0,hc/len) S[i].l=max(1,i*len),S[i].r=min(hc,(i+1)*len-1),S[i].Init();
rep(i,1,n+1) {
cmax(Ans,1ll*(n-i+1)*A[i].first+ans);
if(i<=n) Add(A[i].second);
}
printf("%lld
",Ans);
}