Submission #623720

#TimeUsernameProblemLanguageResultExecution timeMemory
623720jophyyjhFinding Routers (IOI20_routers)C++14
100 / 100
5 ms980 KiB
/**
 * Well, if we naively do binary search per router, the score is ~70. After that, I
 * was thinking if we can minimize the num. of queries by "re-using" information.
 * use_detector() is clearly an increasing function, so
 *              use_detector(l) = use_detector(r) 
 *        => use_detector(l) = use_detector(l+1) = ... = use_detector(r).
 * This means we can do some form of caching, though its efficiency is unknown. Also,
 * we do not know whether the grader is adaptive, but I'm pretty sure it can reduce
 * the num. of queries significantly.
 * 
 * Again, our task is to find the last point v with use_detector(v) = x (for each x).
 * First, check a point every x points, and call these checked points "initial pts".
 * Now, the "endpoints" we're finding must be between two initial points, so we can
 * do binary jump for each one of them. The num of steps is l/x + n * log_2(x). The
 * optimal x is 69, which yields at most 7557 steps (worst case). Good enough~
 * 
 * Steps needed:  n/x * log(x), x = 69  (~7500 steps, for [S4], but with caching)
 * Implementation 1.6
*/

#include <bits/stdc++.h>
#include "routers.h"

typedef std::vector<int>  vec;

const int X = 69;


std::map<int, int> cache1;
std::map<int, int, std::greater<int>> cache2;

int wrap_use(int i) {
    auto it1 = cache1.lower_bound(i);
    auto it2 = cache2.lower_bound(i);
    if (it1 != cache1.end() && it2 != cache2.end()) {
        if (it1->second == it2->second)
            return it1->second;
    }
    int result = use_detector(i);
    cache1[i] = cache2[i] = result;
    return result;
}

vec find_routers(int l, int n, int q) {
    cache1.clear();
    cache2.clear();
    if (l == 100000 && n == 1000 && q == 20000) {
        vec init_pt = {0}, init_ask = {0};
        for (int i = X; i < l; i += X) {
            init_pt.push_back(i);
            init_ask.push_back(wrap_use(i));
        }
        init_pt.push_back(l);
        init_ask.push_back(n - 1);
        int m = init_pt.size();
        vec midpoints;
        for (int i = 0; i < m - 1; i++) {
            int a = init_pt[i], b = init_pt[i + 1];
            int left = init_ask[i], right = init_ask[i + 1];
            while (left < right) {
                int j1 = a, j2 = b;
                while (j1 < j2) {
                    int mid = (j1 + j2 + 1) / 2;
                    if (wrap_use(mid) == left)
                        j1 = mid;
                    else
                        j2 = mid - 1;
                }
                midpoints.push_back(j1);
                a = j1 + 1, left++;
            }
        }
        vec ans = {0};
        for (int mi : midpoints)
            ans.push_back(2 * mi - ans.back());
        return ans;
    } else {
        vec ans = {0};
        for (int i = 0; i < n - 1; i++) {
            int prev = ans.back();
            int a = prev, b = l;
            while (a < b) {
                int mid = (a + b + 1) / 2;
                if (wrap_use(mid) == i)
                    a = mid;
                else
                    b = mid - 1;
            }
            ans.push_back(2 * a - prev);
        }
        return ans;
    }
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...