Submission #1249452

#TimeUsernameProblemLanguageResultExecution timeMemory
1249452_is_this_fft_Souvenirs (IOI25_souvenirs)C++20
100 / 100
13 ms412 KiB
#include "souvenirs.h"
#include <vector>
#include <cassert>

using namespace std;

typedef long long ll;

struct Row {
  vector<int> present;
  ll sum;

  Row () { }
  
  Row (int _n, vector<int> _items, ll _sum) : present(_n, 0), sum(_sum) {
    for (int u : _items) {
      present[u] = 1;
    }
  }

  int count () {
    int ans = 0;
    for (int u : present)
      ans += u;
    return ans;
  }

  bool exists () {
    return !present.empty();
  }

  void operator-= (const Row& other) {
    for (int i = 0; i < (int) present.size(); i++)
      present[i] -= other.present[i];

    sum -= other.sum;
  }
};

void reduce_and_insert (vector<Row> &lead, Row &row) {
  int n = lead.size();
  
  for (int j = 0; j < n; j++) {
    if (lead[j].exists() && lead[j].count() == 1 && row.present[j]) {
      row -= lead[j];
    }
  }

  int nk = -1;
  for (int j = 0; j < n; j++) {
    if (row.present[j]) {
      nk = j;
      break;
    }
  }

  assert(nk != -1);
  assert(!lead[nk].exists());

  lead[nk] = row;
}

pair<vector<int>, ll> transaction_wrap (ll m, vector<int> &bought) {
  auto pr = transaction(m);
  for (int u : pr.first)
    bought[u]++;
  return pr;
}

void buy_souvenirs (int n, ll p0) {
  vector<Row> lead (n);
  vector<int> bought (n, 0);
  
  ll nxt = p0 - 1;
  while (true) {
    auto pr = transaction_wrap(nxt, bought);
    Row row (n, pr.first, nxt - pr.second);
    lead[pr.first[0]] = row;

    if (pr.first.size() == 1) {
      nxt = row.sum - 1;
    } else {
      nxt = row.sum / pr.first.size();
    }

    if (pr.first.size() == 1 && pr.first[0] == n - 1) {
      break;
    }
  }

  // initialization is complete
  while (true) {
    // pseudo-Gaussian elimination
    for (int i = n - 1; i >= 0; i--) {
      if (!lead[i].exists() || lead[i].count() != 1)
        continue;

      for (int j = i - 1; j >= 0; j--) {
        if (lead[j].exists() && lead[j].present[i]) {
          lead[j] -= lead[i];
        }
      }
    }

    // if everyone has a lead, then we are done
    bool finished = true;
    for (int i = 1; i < n; i++) {
      if (!lead[i].exists())
        finished = false;
    }

    if (finished)
      break;

    bool ee = false;
    for (int i = n - 1; i >= 0; i--) {
      if (!lead[i].exists())
        ee = true;
      
      if (lead[i].exists() && lead[i].count() > 1) {
        ll ask = lead[i].sum / lead[i].count();
        auto pr = transaction_wrap(ask, bought);
        Row row (n, pr.first, ask - pr.second);
        reduce_and_insert(lead, row);
        
        break;
      }

      if (lead[i].exists() && lead[i].count() == 1 && ee) {
        ll ask = lead[i].sum - 1;
        auto pr = transaction_wrap(ask, bought);
        Row row (n, pr.first, ask - pr.second);
        reduce_and_insert(lead, row);

        break;
      }
    }
  }

  for (int i = 0; i < n; i++) {
    while (bought[i] < i) {
      transaction_wrap(lead[i].sum, bought);
    }
  }
}
#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...