SMU Summer 2023 Contest Round 3 Solutions

A. Curriculum Vitae

The problem requires finding the longest subsequence where digit 1 is never followed by digit 0. This is equivalent to finding the longest non-decreasing subsequence in a binary sequence. An alternative approach uses prefix sums to count zeros and suffix sums to count ones.

#include  <bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    vector<int> arr(n);
    for(int i = 0; i < n; i++){
        cin >> arr[i];
    }
    
    vector<int> zeroPrefix(n + 1, 0);
    vector<int> oneSuffix(n + 2, 0);
    
    for(int i = 0; i < n; i++){
        zeroPrefix[i + 1] = zeroPrefix[i] + (arr[i] == 0 ? 1 : 0);
    }
    for(int i = n - 1; i >= 0; i--){
        oneSuffix[i] = oneSuffix[i + 1] + (arr[i] == 1 ? 1 : 0);
    }
    
    int result = 0;
    for(int i = 0; i <= n; i++){
        int zeros = zeroPrefix[i];
        int ones = oneSuffix[i];
        result = max(result, zeros + ones);
    }
    cout << result << endl;
    return 0;
}

Alternatively, a dynamic programming approach can solve this in O(n²):

#include  <bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    vector<int> data(n);
    for(int i = 0; i < n; i++) cin >> data[i];
    
    vector<int> dp(n, 1);
    int maxLen = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < i; j++){
            if(data[i] >= data[j]){
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
        maxLen = max(maxLen, dp[i]);
    }
    cout << maxLen << endl;
    return 0;
}

B. Math Show

With small constraints, a brute force greedy solution works effectively. The key insight is to first determine how many complete problem sets can be finished, then greedily assign remaining problems to maximize score.

#include  <bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, k, M;
    cin >> n >> k >> M;
    vector<int> problems(k + 1);
    int totalTime = 0;
    for(int i = 1; i <= k; i++){
        cin >> problems[i];
        totalTime += problems[i];
    }
    sort(problems.begin() + 1, problems.end());
    
    int bestScore = 0;
    for(int completed = 0; completed <= n; completed++){
        int timeUsed = totalTime * completed;
        int currentScore = completed * k + completed;
        
        if(timeUsed > M) break;
        
        int remaining = n - completed;
        for(int idx = 1; idx <= k && timeUsed < M; idx++){
            int assignments = remaining;
            while(assignments > 0 && timeUsed < M){
                timeUsed += problems[idx];
                if(timeUsed <= M){
                    currentScore++;
                }
                assignments--;
            }
        }
        bestScore = max(bestScore, currentScore);
    }
    cout << bestScore << endl;
    return 0;
}

C. Four Segments

The goal is to find indices i, j, k that maximize:

$$\text{sum}(0, i) - \text{sum}(i, j) + \text{sum}(j, k) - \text{sum}(k, n)$$

where sum(a, b) represents the prefix sum difference. Notice that when j is fixed, the expression splits into two independent parts: the first depends only on i, and the second depends only on k. This allows us to compute optimal i and k for each j independently.

#include  <bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    vector<int> arr(n + 1);
    for(int i = 1; i <= n; i++) cin >> arr[i];
    
    vector<int> prefix(n + 1, 0);
    for(int i = 1; i <= n; i++) prefix[i] = prefix[i - 1] + arr[i];
    
    int answer = LLONG_MIN;
    int bestI = 0, bestJ = 0, bestK = 0;
    
    for(int j = 0; j <= n; j++){
        int leftBest = LLONG_MIN;
        int leftIdx = 0;
        for(int i = 0; i <= j; i++){
            int value = prefix[i] - (prefix[j] - prefix[i]);
            if(value > leftBest){
                leftBest = value;
                leftIdx = i;
            }
        }
        
        int rightBest = LLONG_MIN;
        int rightIdx = 0;
        for(int k = j; k <= n; k++){
            int value = prefix[k] - prefix[j] - (prefix[n] - prefix[k]);
            if(value > rightBest){
                rightBest = value;
                rightIdx = k;
            }
        }
        
        int total = leftBest + rightBest;
        if(total > answer){
            answer = total;
            bestI = leftIdx;
            bestJ = j;
            bestK = rightIdx;
        }
    }
    cout << bestI << ' ' << bestJ << ' ' << bestK << endl;
    return 0;
}

D. Monitor

Given q defective pixels with coordinates and timestamps, determine earliest moment when these defects form a k×k square. If no such configuration exists, output -1.

The solution uses binary search on time, combined with 2D prefix sums to efficiently check whether any k×k square is completely filled.

#include  <bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;

struct Defect {
    int x, y, time;
};

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int rows, cols, k, q;
    cin >> rows >> cols >> k >> q;
    
    vector<Defect> defects(q + 1);
    for(int i = 1; i <= q; i++){
        cin >> defects[i].x >> defects[i].y >> defects[i].time;
    }
    
    sort(defects.begin() + 1, defects.end(), [](Defect a, Defect b){
        return a.time < b.time;
    });
    
    auto validate = [&](vector<vector<int>>& grid) -> bool {
        for(int i = k; i <= rows; i++){
            for(int j = k; j <= cols; j++){
                int count = grid[i][j] - grid[i - k][j] - grid[i][j - k] + grid[i - k][j - k];
                if(count == k * k) return true;
            }
        }
        return false;
    };
    
    int left = 1, right = q;
    while(left <= right){
        int mid = (left + right) >> 1;
        
        vector<vector<int>> status(rows + 1, vector<int>(cols + 1, 0));
        for(int i = 1; i <= mid; i++){
            status[defects[i].x][defects[i].y] = 1;
        }
        
        for(int i = 1; i <= rows; i++){
            for(int j = 1; j <= cols; j++){
                status[i][j] += status[i - 1][j] + status[i][j - 1] - status[i - 1][j - 1];
            }
        }
        
        if(validate(status)){
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    
    cout << (left > q ? -1 : defects[left].time) << endl;
    return 0;
}

The binary search finds the minimum threshold where the condition becomes satisfied. If the search exhausts all q defects without finding a valid square, the output is -1.

Tags: contest DP Binary Search prefix sum greedy

Posted on Tue, 30 Jun 2026 18:04:49 +0000 by daz1034