Submission #314863

#TimeUsernameProblemLanguageResultExecution timeMemory
314863model_code3D Histogram (COCI20_histogram)C++17
110 / 110
849 ms67736 KiB
#include <bits/stdc++.h>
using namespace std;
#define TRACE(x) cerr << #x << " = " << x << endl
#define FOR(i, a, b) for(int i = (a); i < (b); i++)
#define REP(i, n) FOR(i, 0, n)

typedef long long int llint;

typedef pair<llint, llint> par;

#define X first
#define Y second

const int MAXN = 5e5 + 10;
const int OFF = 1 << 21;

llint ccw(par a, par b, par c) {
  return a.X * (b.Y - c.Y) + b.X * (c.Y - a.Y) + c.X * (a.Y - b.Y);
}

int n;
int A[MAXN], B[MAXN];

llint rj;

// to je onaj slucaj kad mi lijeva granica l intervla
// [l, r] odredjuje minimum na intervalu za oba niza A i B
// dakle, treba napraviti foru s 2 pointera
void prvi_slucaj(int lo, int mid, int hi) {
  int r = mid;
  int a_mini = A[mid], b_mini = B[mid];
  for(int l = mid; l >= lo; l--) {
    a_mini = min(a_mini, A[l]);
    b_mini = min(b_mini, B[l]);
    while(r < hi && A[r + 1] >= a_mini && B[r + 1] >= b_mini) r++;
    rj = max(rj, (llint) a_mini * b_mini * (r - l + 1));
  }
}

vector<par> tocke;

int off;
vector<par> hullovi[OFF];
int trenutni[OFF];

void obrisi_stari_tournament() {
  REP(i, 2 * off)
    hullovi[i].clear();
}

void rekurzivno_izradi_hullove(int cvor, int a, int b) {
  if(a == b) {
    if(a < (int) tocke.size()) 
      hullovi[cvor].push_back(tocke[a]);
    trenutni[cvor] = (int) hullovi[cvor].size() - 1;
    return;
  }
  rekurzivno_izradi_hullove(2 * cvor, a, (a + b) / 2);
  rekurzivno_izradi_hullove(2 * cvor + 1, (a + b) / 2 + 1, b);
  hullovi[cvor] = hullovi[2 * cvor];
  int poc = (a + b) / 2 + 1, kraj = min(b + 1, (int) tocke.size());
  FOR(i, poc, kraj) {
    int sz = (int) hullovi[cvor].size();
    while(sz >= 2 && ccw(hullovi[cvor][sz - 2], hullovi[cvor][sz - 1], tocke[i]) <= 0) {
      hullovi[cvor].pop_back();
      sz--;
    }
    hullovi[cvor].push_back(tocke[i]);
  }
  trenutni[cvor] = (int) hullovi[cvor].size() - 1;
}

void izgradi_tournament_hullova() {
  for(off = 1; off < (int) tocke.size(); off *= 2);
  obrisi_stari_tournament();
  rekurzivno_izradi_hullove(1, 0, off - 1);
}

//prilagodjeni dp, buduci da je query tocka uvijek oblika (t, 1)
llint dp(int t, par a) {
  return t * a.X + a.Y;
}

llint ispitaj_hull(int cvor, int t) {
  if(trenutni[cvor] == -1) return 0;
  while(trenutni[cvor] > 0 && dp(t, hullovi[cvor][trenutni[cvor] - 1]) >= dp(t, hullovi[cvor][trenutni[cvor]]))
    trenutni[cvor]--;
  return dp(t, hullovi[cvor][trenutni[cvor]]);
}

llint query(int cvor, int a, int b, int lo, int hi, int t) {
  if(a > hi || b < lo) return 0;
  if(a >= lo && b <= hi) return ispitaj_hull(cvor, t);
  llint lijevi = query(2 * cvor, a, (a + b) / 2, lo, hi, t);
  llint desni = query(2 * cvor + 1, (a + b) / 2 + 1, b, lo, hi, t);
  return max(lijevi, desni);
}

// to je kompliciraniji slucaj kad lijeva granica l 
// intervala [l, r]  odredjuje minimum za A, 
// a densa r minimum za B
void treci_slucaj(int lo, int mid, int hi) { 
  //ovdje mislim na minimume na lijevoj strani
  int a_mini = A[mid], b_mini = B[mid];
  //pocetak i kraj intervala koji pridruzujemo trenutom l
  //interval je oblika [poc, kraj]
  int poc = mid, kraj = mid;
  vector<par> intervali;
  vector<int> faktori;
  for(int l = mid; l >= lo; l--) { 
    a_mini = min(a_mini, A[l]);
    b_mini = min(b_mini, B[l]);
    while(kraj < hi && A[kraj + 1] >= a_mini) kraj++;
    while(poc <= hi && B[poc] > b_mini) poc++;
    intervali.push_back(par(poc - mid, kraj - mid));
    faktori.push_back(a_mini);
  }
  //postavljam tocke nad kojima cu graditi hullove
  tocke.clear();
  int mini_desno = B[mid];
  for(int r = mid; r <= hi; r++) {
    mini_desno = min(mini_desno, B[r]);
    tocke.push_back(par(mini_desno, (llint) mini_desno * (r + 1)));
  }
  
  izgradi_tournament_hullova();

  //upiti:
  for(int l = mid; l >= lo; l--) {
    int i = mid - l;
    rj = max(rj, (llint) faktori[i] * query(1, 0, off - 1, intervali[i].X, intervali[i].Y, -1 * l));
  }
}

void divide_and_conquer(int lo, int hi, int lijevo) {
  if(lo > hi) return;
  if(lijevo) { 
    int mid = (lo + hi) / 2; 
    prvi_slucaj(lo, mid, hi);
    treci_slucaj(lo, mid, hi);
    if(lo == hi) return;
    divide_and_conquer(lo, mid - 1, lijevo);
    divide_and_conquer(mid + 1, hi, lijevo);
  }
  else { //desno 
    int mid = (lo + hi + 1) / 2; 
    prvi_slucaj(lo, mid, hi);
    treci_slucaj(lo, mid, hi);
    if(lo == hi) return;
    divide_and_conquer(mid + 1, hi, lijevo);
    divide_and_conquer(lo, mid - 1, lijevo);
  }
}

void rijesi() {
  divide_and_conquer(0, n - 1, 1);
  reverse(A, A + n); reverse(B, B + n);
  divide_and_conquer(0, n - 1, 0);
}

int main() {
  scanf("%d", &n);
  REP(i, n) 
    scanf("%d %d", &A[i], &B[i]);
  rijesi();
  printf("%lld\n", rj);
  return 0;
}

Compilation message (stderr)

histogram.cpp: In function 'int main()':
histogram.cpp:162:8: warning: ignoring return value of 'int scanf(const char*, ...)' declared with attribute 'warn_unused_result' [-Wunused-result]
  162 |   scanf("%d", &n);
      |   ~~~~~^~~~~~~~~~
histogram.cpp:164:10: warning: ignoring return value of 'int scanf(const char*, ...)' declared with attribute 'warn_unused_result' [-Wunused-result]
  164 |     scanf("%d %d", &A[i], &B[i]);
      |     ~~~~~^~~~~~~~~~~~~~~~~~~~~~~
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...