Submission #1361015

#TimeUsernameProblemLanguageResultExecution timeMemory
1361015tickcrossySoccer Stadium (IOI23_soccer)C++20
100 / 100
4462 ms78856 KiB
#include <vector>
#include <algorithm>

using namespace std;

int biggest_stadium(int N, std::vector<std::vector<int>> F) {
    long long total_empty = 0;
    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < N; ++j) {
            if (F[i][j] == 0) total_empty++;
        }
    }
    // 特判:全图无树满球场,或无空地
    if (total_empty == N * N) return N * N;
    if (total_empty == 0) return 0;

    // 为了最大化 CPU 缓存命中率与触发内存连续预取,全部采用 1D 拉平数组
    vector<int> L_row(N * N);
    vector<int> R_row(N * N);
    
    // 预处理每行对于每个位置其左侧/右侧最近边界(或树)的索引限制
    for (int r = 0; r < N; ++r) {
        int last = 0;
        int row_offset = r * N;
        for (int c = 0; c < N; ++c) {
            if (F[r][c] == 1) last = c + 1;
            L_row[row_offset + c] = last;
        }
        last = N - 1;
        for (int c = N - 1; c >= 0; --c) {
            if (F[r][c] == 1) last = c - 1;
            R_row[row_offset + c] = last;
        }
    }

    // dp 数组维持二维拉平:dp[d_offset + c] 代表了处理中的当前上界 u 和 下界 d 时涵盖了合规核心列的最广面积
    vector<int> dp(N * N, 0);
    vector<int> L(N), R(N);

    // 外层逆向枚举上边界 u
    for (int u = N - 1; u >= 0; --u) {
        int u_offset = u * N;
        // 初始情况:区间仅包含单独的一行 u (即 d = u)
        for (int c = 0; c < N; ++c) {
            L[c] = L_row[u_offset + c];
            R[c] = R_row[u_offset + c];
            int w = R[c] - L[c] + 1;
            dp[u_offset + c] = w > 0 ? w : 0;
        }
        
        // 中层顺向枚举下边界 d
        for (int d = u + 1; d < N; ++d) {
            int d_offset = d * N;
            int prev_offset = (d - 1) * N;
            
            const int* l_ptr = &L_row[d_offset];
            const int* r_ptr = &R_row[d_offset];
            int* dp_d = &dp[d_offset];
            const int* dp_prev = &dp[prev_offset];
            
            // 内层由核心列 c 担当,完全无分支 + SIMD 向量化强行提速
            // 通过无脑 max(0, width) 消除了 if (w <= 0) break; 带来的流水线惩罚,完美继承断层推演
            #pragma GCC ivdep
            for (int c = 0; c < N; ++c) {
                int l_val = L[c];
                int l_new = l_ptr[c];
                L[c] = l_val > l_new ? l_val : l_new;
                
                int r_val = R[c];
                int r_new = r_ptr[c];
                R[c] = r_val < r_new ? r_val : r_new;
                
                int w = R[c] - L[c] + 1;
                w = w > 0 ? w : 0;
                
                int v1 = dp_d[c];    // 来自之前的 u (即 u+1), 目前还是旧值:代表 [u+1, d]
                int v2 = dp_prev[c]; // 刚刚处理过的本次的 u,d-1 的新值:代表 [u, d-1]
                int mx = v1 > v2 ? v1 : v2;
                
                dp_d[c] = w + mx;    // 原地覆盖更新为全新的 [u, d]    
            }
        }
    }

    // 整个矩阵收缩归纳完成,对于任意核心列 c 峰值都最终浮现于顶层 [0, N-1] 区间之中
    int ans = 0;
    int last_offset = (N - 1) * N;
    for (int c = 0; c < N; ++c) {
        if (dp[last_offset + c] > ans) {
            ans = dp[last_offset + c];
        }
    }

    return ans;
}
#Result Execution timeMemoryGrader output
Fetching results...
#Result Execution timeMemoryGrader output
Fetching results...
#Result Execution timeMemoryGrader output
Fetching results...
#Result Execution timeMemoryGrader output
Fetching results...
#Result Execution timeMemoryGrader output
Fetching results...
#Result Execution timeMemoryGrader output
Fetching results...
#Result Execution timeMemoryGrader output
Fetching results...