Problem: icpc2024_pacific_northwest_div1_h
Difficulty: easy
Categories: game, math
Time limit: 1000 ms, memory limit: 256 MB

Alice was recently given a homework assignment to write a program that would output the number of inversions in any subarray of a given permutation. She happily turned it in and got full marks on her assignment. The next week, their homework assignment was to find the length of the longest increasing subsequences in the same array. Unfortunately, Alice had already thrown away the paper that contained the permutation she needed.

Luckily, this permutation was stored in the program she wrote. Unfortunately, she is now only able to query for the number of inversions in the subarrays of the permutation. As class is close to starting, she asks you for help to solve this problem in a timely manner.

A sequence $a$ is a subsequence of an array $b$ if it is possible to delete some (possibly zero) elements from $b$ to get $a$ . A sequence is increasing if every element is strictly greater than all preceding ones, and the LLIS of an array is the length of the longest increasing subsequence.

# Interaction

This is an interactive problem.

Interaction starts by reading a single integer $n$ $( 1 \leq n \leq 1 0 ^ { 3 }$ ), the length of the permutation $p$

You are then able to make at most $n$ queries of the form ? l r where $1 \leq l \leq r \leq n$ . Each query should be on a single line. After each query, read in a single integer, the number of inversions in the subarray $[ l , r ]$ . An inversion is a pair of integers $( x , y )$ where $l \leq x < y \leq r$ and $p _ { x } > p _ { y }$ .

When you have determined the LLIS, output the answer in the form: ! ans on a single line, where ans is the LLIS. After outputting the answer, your program should exit. If you attempt to read a response after outputting an answer, you will receive an arbitrary verdict.

Do not forget to flush the output after each query you output.

The interactor is not adaptive: When the interaction begins, the permutation $p$ is already determined. It is guaranteed that each integer from 1 to $n$ appears exactly once.

If the interactor receives any invalid or unexpected input, the interactor will output $- 1$ and then immediately terminate. Your program should cleanly exit in order to receive a Wrong Answer verdict, otherwise the verdict that it receives may be an arbitrary verdict indicating that your submission is incorrect.

If your program terminates before outputting the answer, your submission will receive an arbitrary verdict indicating that your submission is incorrect.

You are provided with a command-line tool for local testing. The tool has comments at the top to explain its use.

# Read

Write

Sample Interaction 1   

<table><tr><td rowspan=1 colspan=2>3</td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>？13</td></tr><tr><td rowspan=1 colspan=1>1</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>？12</td></tr><tr><td rowspan=1 colspan=1>1</td><td rowspan=1 colspan=1></td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>！2</td></tr></table>

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;


static long long queries = 0;
static long long query_limit = 0;



void log_metrics() {
    try {
        tout << "queries=" << queries << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {
        
    }
}


void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}



int calc_llis(const vector<int>& p) {
    if (p.empty()) return 0;
    vector<int> tails;
    for (int x : p) {
        auto it = lower_bound(tails.begin(), tails.end(), x);
        if (it == tails.end()) {
            tails.push_back(x);
        } else {
            *it = x;
        }
    }
    return (int)tails.size();
}



long long count_inversions(const vector<int>& sub) {
    if (sub.empty()) return 0;
    int max_val = 0;
    for (int x : sub) max_val = max(max_val, x);

    
    vector<int> bit(max_val + 1, 0);

    auto update = [&](int idx, int val) {
        for (; idx <= max_val; idx += idx & -idx) bit[idx] += val;
    };
    
    auto query = [&](int idx) {
        int res = 0;
        for (; idx > 0; idx -= idx & -idx) res += bit[idx];
        return res;
    };

    long long inv_count = 0;
    for (size_t i = 0; i < sub.size(); ++i) {
        int x = sub[i];
        
        
        
        inv_count += (i - query(x));
        update(x, 1);
    }
    return inv_count;
}

int main(int argc, char** argv) {
    
    registerInteraction(argc, argv);
    atexit(log_metrics);

    
    int n = inf.readInt(1, 1000, "n");
    vector<int> p(n);
    vector<bool> seen(n + 1, false);
    for (int i = 0; i < n; ++i) {
        p[i] = inf.readInt(1, n, format("p[%d]", i));
        if (seen[p[i]]) {
            finish(_fail, format("Duplicate value %d in permutation", p[i]));
        }
        seen[p[i]] = true;
    }

    
    query_limit = n;

    
    
    
    int hard_cap = max(2000, n * 5);

    
    cout << n << endl;

    while (true) {
        string token;
        
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF: Contestant terminated without providing an answer");
        }

        if (token == "!") {
            
            int ans;
            if (!(cin >> ans)) {
                finish(_wa, "Failed to read integer answer after '!'");
            }

            int expected = calc_llis(p);
            if (ans == expected) {
                finish(_ok, format("Correct. LLIS is %d.", ans));
            } else {
                finish(_wa, format("Wrong answer. Expected %d, found %d.", expected, ans));
            }
        } else {
            
            int l, r;
            if (token == "?") {
                
                if (!(cin >> l >> r)) {
                    finish(_wa, "Failed to read query parameters l r after '?'");
                }
            } else {
                
                try {
                    size_t idx = 0;
                    l = stoi(token, &idx);
                    if (idx != token.size()) {
                        cout << -1 << endl;
                        finish(_wa, "Invalid token format: " + token);
                    }
                } catch (...) {
                    cout << -1 << endl;
                    finish(_wa, "Invalid token (expected integer or '?'): " + token);
                }
                
                if (!(cin >> r)) {
                    finish(_wa, "Failed to read parameter r");
                }
            }

            queries++;
            
            if (queries % 10 == 0) log_metrics();

            
            if (l < 1 || l > n || r < 1 || r > n || l > r) {
                cout << -1 << endl;
                finish(_wa, format("Query range [%d, %d] is invalid (n=%d)", l, r, n));
            }

            
            if (queries > hard_cap) {
                finish(_pe, format("Query hard cap (%d) exceeded. Limit was %d.", hard_cap, query_limit));
            }

            
            vector<int> sub;
            sub.reserve(r - l + 1);
            for (int i = l - 1; i < r; ++i) {
                sub.push_back(p[i]);
            }
            long long result = count_inversions(sub);

            cout << result << endl;
        }
    }

    return 0;
}


Adaptive interactor (C++):

Test generator (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);

    string mode = opt<string>("mode", "non");
    int n_opt = opt<int>("n", -1);
    
    
    long long seed_num = -1;
    try {
        string s = argv[1];
        if (!s.empty() && all_of(s.begin(), s.end(), ::isdigit)) {
            seed_num = stoll(s);
        }
    } catch (...) {
        seed_num = -1;
    }

    int n;
    vector<int> p;

    
    if (seed_num >= 1 && seed_num <= 3) {
        if (seed_num == 1) {
            n = 1; 
            p = {1};
        } else if (seed_num == 2) {
            n = 5;
            p = {1, 2, 3, 4, 5};
        } else {
            n = 5;
            p = {5, 4, 3, 2, 1};
        }
    } else {
        
        if (n_opt != -1) {
            n = n_opt;
        } else {
            
            if (rnd.next(3) == 0) n = rnd.next(1, 100);
            else n = rnd.next(900, 1000);
        }

        p.resize(n);
        iota(p.begin(), p.end(), 1);

        int type = rnd.next(1, 5);
        if (type == 1) {
            
            shuffle(p.begin(), p.end());
        } else if (type == 2) {
            
            int k = rnd.next(0, (int)sqrt(n) + 2);
            for(int i = 0; i < k; ++i) {
                int a = rnd.next(n);
                int b = rnd.next(n);
                swap(p[a], p[b]);
            }
        } else if (type == 3) {
            
            reverse(p.begin(), p.end());
            int k = rnd.next(0, (int)sqrt(n) + 2);
            for(int i = 0; i < k; ++i) {
                int a = rnd.next(n);
                int b = rnd.next(n);
                swap(p[a], p[b]);
            }
        } else if (type == 4) {
            
            p.clear();
            int base_k = (int)sqrt(n);
            if (base_k < 1) base_k = 1;
            int cur = 1;
            while (cur <= n) {
                int rem = n - cur + 1;
                
                int sz = rnd.next(1, min(rem, max(2, base_k * 2)));
                
                for (int i = 0; i < sz; ++i) {
                    p.push_back(cur + sz - 1 - i);
                }
                cur += sz;
            }
            
            int swaps = rnd.next(0, 5);
            for(int i = 0; i < swaps; ++i) swap(p[rnd.next(n)], p[rnd.next(n)]);
        } else {
            
            int l = rnd.next(0, n - 1);
            int r = rnd.next(0, n - 1);
            if (l > r) swap(l, r);
            reverse(p.begin() + l, p.begin() + r + 1);
        }
    }

    if (mode == "non") {
        
        cout << n << endl;
        for (int i = 0; i < n; ++i) {
            cout << p[i] << (i == n - 1 ? "" : " ");
        }
        cout << endl;
    } else {
        
        cout << n << endl;
    }

    return 0;
}