Submission #839755

#TimeUsernameProblemLanguageResultExecution timeMemory
839755model_codeSoccer Stadium (IOI23_soccer)C++17
100 / 100
2330 ms453208 KiB
// correct/authors_fullscore.cpp

// ##################################
// ##                              ##
// ##   Solution for Subtask B6    ##
// ##        (100.00 pts)          ##
// ##                              ##
// ##################################

#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>

#include "soccer.h"

using namespace std;

class RangeMin
{
public:
    int size_ = 1;
    vector<int> dat;

    void init(int sz)
    {
        while (size_ <= sz)
            size_ *= 2;
        dat.resize(size_ * 2, 0);
    }
    void update(int pos, int x)
    {
        pos += size_;
        dat[pos] = x;
        while (pos >= 2)
        {
            pos >>= 1;
            dat[pos] = min(dat[pos * 2], dat[pos * 2 + 1]);
        }
    }
    int query_(int l, int r, int a, int b, int u)
    {
        if (r <= a || b <= l)
            return 1000000000;
        if (l <= a && b <= r)
            return dat[u];
        int v1 = query_(l, r, a, ((a + b) >> 1), u * 2);
        int v2 = query_(l, r, ((a + b) >> 1), b, u * 2 + 1);
        return min(v1, v2);
    }
    int query(int l, int r)
    {
        return query_(l, r, 0, size_, 1);
    }
};

struct Rectangle
{
    int xl, xr, yl, yr;
};

bool operator<(const Rectangle &a1, const Rectangle &a2)
{
    if (a1.xr - a1.xl < a2.xr - a2.xl)
        return true;
    if (a1.xr - a1.xl > a2.xr - a2.xl)
        return false;
    if (a1.xl < a2.xl)
        return true;
    if (a1.xl > a2.xl)
        return false;
    if (a1.xr < a2.xr)
        return true;
    if (a1.xr > a2.xr)
        return false;
    if (a1.yl < a2.yl)
        return true;
    if (a1.yl > a2.yl)
        return false;
    if (a1.yr < a2.yl)
        return true;
    return false;
}

bool operator==(const Rectangle &a1, const Rectangle &a2)
{
    if (a1.xl == a2.xl && a1.xr == a2.xr && a1.yl == a2.yl && a1.yr == a2.yr)
        return true;
    return false;
}

const int MAXN = 3209;

// Variables for Rectangles & DP
int NumRects = 0;
Rectangle Rect[MAXN * MAXN];
vector<int> Next[MAXN * MAXN];
int dp[MAXN * MAXN];

// Other Variables
int EmptyUp[MAXN][MAXN];
int EmptyDn[MAXN][MAXN];
RangeMin UpMax[MAXN];
RangeMin DnMin[MAXN];
vector<int> List[MAXN];

// Enumerate Rectangles
void Enumerate(int N, int bottom, vector<int> Up, vector<int> Dn)
{
    stack<pair<int, int>> Stack;
    Stack.push(make_pair(-1, bottom + 2));
    Up.push_back(bottom);

    // Start Simulation
    int LatestBottom = -1;
    for (int i = 0; i <= N; i++)
    {
        while (Stack.top().second <= Up[i])
        {
            int val = Stack.top().second;
            Stack.pop();
            if (val == Up[i])
                continue;

            // Add Rectangle
            Rectangle New;
            New.yl = Stack.top().first + 1;
            New.yr = i - 1;
            New.xl = val + 1;
            New.xr = bottom;
            if (New.yl <= LatestBottom)
            {
                Rect[NumRects] = New;
                NumRects += 1;
            }
        }
        if (i == N)
            break;

        // Add Stack & Update LatestBottom
        Stack.push(make_pair(i, Up[i]));
        if (Dn[i] == bottom + 1)
            LatestBottom = i;
    }
}

void add_edge_real(int N, int idx, vector<int> CutPoint)
{
    for (int j = 0; j < (int)CutPoint.size() - 1; j++)
    {
        if (CutPoint[j + 1] - CutPoint[j] <= 1)
            continue;
        Rectangle New;
        New.yl = CutPoint[j + 0] + 1;
        New.yr = CutPoint[j + 1] - 1;
        New.xl = 0;
        New.xr = N - 1;
        if (Rect[idx].xl >= 1)
            New.xl = -UpMax[Rect[idx].xl - 1].query(New.yl, New.yr + 1) + 1;
        if (Rect[idx].xr <= N - 2)
            New.xr = DnMin[Rect[idx].xr + 1].query(New.yl, New.yr + 1) - 1;
        int pos1 = lower_bound(Rect, Rect + NumRects, New) - Rect;
        if (pos1 < NumRects && Rect[pos1] == New)
        {
            Next[idx].push_back(pos1);
        }
    }
}

// Add Edges from rectangle idx
void add_edge(int N, int idx)
{
    // Part 1
    if (Rect[idx].xl >= 1)
    {
        int BlockPlace = Rect[idx].xl - 1;
        vector<int> UpList;
        UpList.push_back(Rect[idx].yl - 1);
        int pos = lower_bound(List[BlockPlace].begin(), List[BlockPlace].end(), Rect[idx].yl) - List[BlockPlace].begin();
        while (pos < (int)List[BlockPlace].size() && List[BlockPlace][pos] <= Rect[idx].yr)
        {
            UpList.push_back(List[BlockPlace][pos]);
            pos += 1;
        }
        UpList.push_back(Rect[idx].yr + 1);
        add_edge_real(N, idx, UpList);
    }

    // Part 2
    if (Rect[idx].xr <= N - 2)
    {
        int BlockPlace = Rect[idx].xr + 1;
        vector<int> DnList;
        DnList.push_back(Rect[idx].yl - 1);
        int pos = lower_bound(List[BlockPlace].begin(), List[BlockPlace].end(), Rect[idx].yl) - List[BlockPlace].begin();
        while (pos < (int)List[BlockPlace].size() && List[BlockPlace][pos] <= Rect[idx].yr)
        {
            DnList.push_back(List[BlockPlace][pos]);
            pos += 1;
        }
        DnList.push_back(Rect[idx].yr + 1);
        add_edge_real(N, idx, DnList);
    }
}

int biggest_stadium(int N, vector<vector<int>> C)
{
    // [1] Calculate EmptyUp, EmptyDn
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            if (C[i][j] == 1)
                EmptyUp[i][j] = i;
            else if (i == 0)
                EmptyUp[i][j] = -1;
            else
                EmptyUp[i][j] = EmptyUp[i - 1][j];
        }
    }
    for (int i = N - 1; i >= 0; i--)
    {
        for (int j = 0; j < N; j++)
        {
            if (C[i][j] == 1)
                EmptyDn[i][j] = i;
            else if (i == N - 1)
                EmptyDn[i][j] = N;
            else
                EmptyDn[i][j] = EmptyDn[i + 1][j];
        }
    }

    // [2] Enumerate Rectangle
    for (int i = 0; i < N; i++)
    {
        vector<int> Up(N, -1), Dn(N, N);
        for (int j = 0; j < N; j++)
            Up[j] = EmptyUp[i][j];
        if (i < N - 1)
        {
            for (int j = 0; j < N; j++)
                Dn[j] = EmptyDn[i + 1][j];
        }
        Enumerate(N, i, Up, Dn);
    }
    sort(Rect, Rect + NumRects); // sort by xr-xl

    // [3] Prepare SegmentTree
    for (int i = 0; i < N; i++)
    {
        UpMax[i].init(N + 1);
        DnMin[i].init(N + 1);
        for (int j = 0; j < N; j++)
        {
            UpMax[i].update(j, -EmptyUp[i][j]);
            DnMin[i].update(j, EmptyDn[i][j]);
        }
    }

    // [4] Add "Edges" on graph
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            if (C[i][j] == 1)
                List[i].push_back(j);
        }
    }
    for (int i = 0; i < NumRects; i++)
    {
        add_edge(N, i);
    }

    // [5] Dynamic Programming
    for (int i = 0; i < NumRects; i++)
    {
        dp[i] = max(dp[i], (Rect[i].xr - Rect[i].xl + 1) * (Rect[i].yr - Rect[i].yl + 1));
        for (int to : Next[i])
        {
            int AddArea_X = (Rect[to].xr - Rect[to].xl + 1) - (Rect[i].xr - Rect[i].xl + 1);
            int AddArea_Y = (Rect[to].yr - Rect[to].yl + 1);
            dp[to] = max(dp[to], dp[i] + AddArea_X * AddArea_Y);
        }
    }

    // [6] Return Answer
    int Answer = 0;
    for (int i = 0; i < NumRects; i++)
        Answer = max(Answer, dp[i]);
    return Answer;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...