Rezolvare completă PbInfo #2555 doubletrouble

Legendarul vrăjitor Merlin se distrează în laboratorul său. El are N poțiuni magice (etichetate cu 1, 2, …, N) aranjate pe o singură linie într-o ordine dată. El dorește să le aranjeze în ordine crescătoare. Se va muta o singură poțiune la un moment dat. Prima poțiune mutată va fi cea etichetată cu 1, apoi cea etichetată cu 2 și așa mai departe. Bineînțeles, pentru a rezolva problema, Merlin va apela la magie. Fiecare poțiune poate fi mutată folosind una sau mai multe vrăji, trecând prin una sau mai multe poziții intermediare. Mutând o poțiune de la poziția I în poziția J, vraja va consuma (I-J)2 unități de energie. De exemplu, dacă există șapte poțiuni aranjate astfel 1, 7, 3, 4, 5, 2, 6 și Merlin mută poțiunea (etichetată cu 2) de la cea de-a șasea poziție la a doua poziție atunci noua aranjare va fi 1, 2, 7, 3, 4, 5, 6 cu (6-2)2 unități de energie consumate.

Înainte de a începe distracția, Merlin a stabilit două criterii:
1. El nu dorește să fie prea obosit la sfârșitul zilei, de aceea nu dorește să consume mai mult de E unități de energie.
2. De asemenea el se plictisește repede, de aceea el dorește să rezolve problema cu un număr minim de vrăji.

Cerința

Calculați numărul minim de vrăji pe care le poate folosi Merlin pentru a rezolva problema.

Date de intrare

Fișierul de intrare doubletrouble.in conține pe prima linie două numere întregi pozitive N și E separate printr-un spațiu iar pe cea de-a doua linie N numere întregi pozitive separate prin câte un spațiu, cel de-al K-lea număr reprezentând eticheta poțiunii de pe poziția K, 1 ≤ K ≤ N.

Date de ieșire

Fișierul de ieșire doubletrouble.out conține un singur număr întreg ce reprezintă numărul minim de vrăji folosite de Merlin, sau -1 dacă nu există soluție.

Restricții și precizări

  • 1 ≤ N ≤ 105
  • 1 ≤ E ≤ 1015
  • Pentru 20 de puncte, 0 < N ≤ 300
  • Pentru 30 de puncte, 300 < N ≤ 6000
  • Pentru 20 de puncte, 6000 < N ≤ 30000
  • Pentru 30 de puncte, 30000 < N ≤ 100000

Exemplu

doubletrouble.in

5 6
2 3 5 1 4

doubletrouble.out

3

Explicație

2 3 5 1 4 --> 2 3 1 5 4 --> 1 2 3 5 4 --> 1 2 3 5 4 --> 1 2 3 4 5
A fost folosit un număr minim de trei vrăji. Folosind primele două vrăji, cu 12 și 22 unități de energie, poțiunea 1 a fost mutată în prima poziție. Folosind cea de-a treia vrajă, cu 12 unități de energie, poțiunea 4 a fost mutată în cea de-a patra poziție. Energie totală consumată: 12 + 22 + 12 = 6.

Cum e corect?

cout < "As la info"; cout << "As la info"; cout >> "As la info";

Felicitări! Poți mai mult?

Avem sute de probleme pentru tine, fiecare cu explicații ușor de înțeles.

Greșit, dar nu-i bai!

Antrenează-te cu sutele de probleme pe care ți le-am pregătit. Îți explicăm fiecare problemă în parte.

Rezolvare

Iată rezolvarea de 100 de puncte pentru problema doubletrouble:

#include <vector>
#include <cstdint>
#include <fstream>
using namespace std;

// the minimum energy for a distance and a given number of spells
int64_t energy(int64_t x, int64_t spells) {
    int64_t a = x / spells;
    int64_t r = x % spells;
    return (spells - r)*a*a + r*(a + 1)*(a + 1);
}

int main() {
    ifstream        f("doubletrouble.in");
    ofstream        g("doubletrouble.out");
    int             N; f >> N;
    int64_t         E; f >> E;
    vector<int>     P(N+1), S(N+1), C(N+1), X;

    // compute the initial position for each potion
    for (int i = 1; i <= N; i++) {
        int v; f >> v; P[v] = i;
    }

    // compute the Fenwick array -- add 1 to all elements
    for (int i = 1; i <= N; i++) {
        for (int k = i; k <= N; k += k & -k) S[k]++;
    }

    // for each potion, find the distance to its desired position
    for (int i = 1; i <= N; i++) {
        int x = 0;
        for (int k = P[i]; k <= N; k += k & -k) S[k]--;
        for (int k = P[i]; k  > 0; k -= k & -k) x += S[k];
        C[x]++;
    }

    // extract all distinct distances
    for (int i = 1; i <= N; i++) if (C[i]) X.push_back(i);

    // binary search for the delta of the optimal solution
    int64_t spells  = -1;
    for (int64_t lo = 1, hi = 1LL*N*N; lo <= hi;) {
        int64_t delta = (lo + hi)/2, cnt = 0, e = 0, s = 0;
        int     u = 1, v = 1;
        for (int i = 0; i < X.size(); i++) {
            // update u and v -- the lower and the upper bound
            while (u < X[i] && energy(X[i], u) - energy(X[i], u + 1)  > delta) u++;
            v = max(v, u);
            while (v < X[i] && energy(X[i], v) - energy(X[i], v + 1) >= delta) v++;

            // update the used energy and the number of spells
            e += C[X[i]]*energy(X[i], v);
            s += C[X[i]]*v;

            // in case we have a range, (u, v] producing delta, then keep track of these values,
            // since later they might save a few spells, by trading a spell for delta units of energy
            if (v > 1 && energy(X[i], v - 1) - energy(X[i], v) == delta) cnt += C[X[i]]*(v - u);
        }

        if (e <= E) {
            spells = s - min((E - e)/delta, cnt);
            lo = delta + 1;
        } else {
            hi = delta - 1;
        }
    }
    g << spells;

    return 0;
}

Atenție

Enunțurile afișate pe această pagină aparțin exclusiv site-ului PbInfo. Astfel, pentru ștergerea conținutului, puteți să ne contactați la adresa Adresa de email.

Rezolvarea problemei #2555 doubletrouble

Pe această pagină găsești rezolvarea de 100 de puncte pentru problema #2555 doubletrouble de pe PbInfo.ro. Atenție: nu încurajăm copiatul codului! Totuși, credem cu tărie că analizarea unei soluții corecte este o metodă foarte ușoară de a învăța informatică, astfel că oferim sursele pentru peste 1500 de probleme de pe platforma PbInfo.ro.

Pentru rezolvări PbInfo de la peste 1500 de probleme, vă invităm să intrați pe site-ul nostru!