C2. Skyscrapers (hard version)
题目链接
线段树+分治
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+7;
int n;
long long a[maxn],b[maxn];
typedef pair<long long,int> SgTreeDataType;
struct treenode
{
int L , R ;
SgTreeDataType mi,lazy;
};
treenode tree[maxn*4];
inline void push_up(int o){
if(tree[2*o].mi.first==0)tree[2*o].mi={1e9+7,1e9};
if(tree[2*o+1].mi.first==0)tree[2*o+1].mi={1e9+7,1e9};
if(tree[2*o].mi.first>tree[2*o+1].mi.first){
tree[o].mi=tree[2*o+1].mi;
}else{
tree[o].mi=tree[2*o].mi;
}
}
inline void build_tree(int L , int R , int o)
{
tree[o].L = L , tree[o].R = R;
if(L==R)
tree[o].mi={a[L],L};
if (R > L)
{
int mid = (L+R) >> 1;
build_tree(L,mid,o*2);
build_tree(mid+1,R,o*2+1);
push_up(o);
}
}
inline SgTreeDataType query(int QL,int QR,int o)
{
int L = tree[o].L , R = tree[o].R;
if (QL <= L && R <= QR) return tree[o].mi;
else
{
int mid = (L+R)>>1;
SgTreeDataType mi = {1e9+7,1};
if (QL <= mid){
SgTreeDataType res = query(QL,QR,2*o);
if(res.first<mi.first){
mi=res;
}
}
if (QR > mid){
SgTreeDataType res = query(QL,QR,2*o+1);
if(res.first<mi.first){
mi=res;
}
}
push_up(o);
return mi;
}
}
long long solve(int l,int r){
if(l>r){
return 0L;
}
if(l==r){
b[l]=a[l];
return 1ll*a[l];
}
pair<long long,int> mi = query(l,r,1);
long long sum1 = solve(l,mi.second-1);
long long sum2 = solve(mi.second+1,r);
//cout<<l<<" "<<r<<" "<<mi.second<<" "<<sum1<<" "<<sum2<<" "<<1ll*((r-mi.second-1)+1)*mi.first+mi.first+sum1<<" "<<1ll*((mi.second-1-l)+1)*mi.first+mi.first+sum2<<endl;
if(1ll*((r-mi.second-1)+1)*mi.first+mi.first+sum1>
1ll*((mi.second-1-l)+1)*mi.first+mi.first+sum2){
for(int i=mi.second;i<=r;i++){
b[i]=mi.first;
}
return 1ll*((r-mi.second-1)+1)*mi.first+mi.first+sum1;
}else{
for(int i=l;i<=mi.second;i++){
b[i]=mi.first;
}
return 1ll*((mi.second-1-l)+1)*mi.first+mi.first+sum2;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
build_tree(1,n,1);
solve(1,n);
for(int i=1;i<=n;i++){
cout<<b[i]<<" ";
}
cout<<endl;
}
法2:
显然满足题意的一定是一个单峰序列,于是可以考虑第i个当最高点的时候,左边的最大总和,而这个是可以递推的,因为当mi>=mi-1的时候第i个可以直接取mi加上去,而当mi<mi-1的时候,要把之前所有大于mi的都变成mi,而之前的部分是单增的,于是可以开个单调栈来维护,复杂度O(n),同理右边的最大总和也可以这么算。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 500000+10;
struct Node {
int val, cnt;
} a[maxn];
int m[maxn];
ll f[maxn], g[maxn];
int b[maxn];
int main() {
int n;
scanf("%d", &n);
for(int i=1; i<=n; i++)
scanf("%d", &m[i]);
int k = 0;
for(int i=1; i<=n; i++) {
if(m[i]>=m[i-1]) {
a[++k] = (Node){m[i], 1};
f[i] = f[i-1]+m[i];
}
else {
ll sum = 0;
int num = 0;
while(k>=1 && a[k].val>=m[i]) {
sum += (ll)a[k].val*a[k].cnt;
num += a[k].cnt; k--;
}
a[++k] = (Node){m[i], num+1};
f[i] = f[i-1]-sum+(ll)(num+1)*m[i];
}
}
k = 0;
for(int i=n; i>=1; i--) {
if(m[i]>=m[i+1]) {
a[++k] = (Node){m[i], 1};
g[i] = g[i+1]+m[i];
}
else {
ll sum = 0;
int num = 0;
while(k>=1 && a[k].val>=m[i]) {
sum += (ll)a[k].val*a[k].cnt;
num += a[k].cnt; k--;
}
a[++k] = (Node){m[i], num+1};
g[i] = g[i+1]-sum+(ll)(num+1)*m[i];
}
}
int id = 1;
for(int i=2; i<=n; i++) {
if(f[i]+g[i]-m[i]>f[id]+g[id]-m[id])
id = i;
}
b[id] = m[id];
for(int i=id-1; i>=1; i--)
b[i] = min(m[i], b[i+1]);
for(int i=id+1; i<=n; i++)
b[i] = min(m[i], b[i-1]);
for(int i=1; i<=n; i++)
printf("%d ", b[i]);
printf("\n");
return 0;
}