Cod sursa(job #1385529)

Utilizator mgntMarius B mgnt Data 12 martie 2015 00:43:13
Problema 12-Perm Scor 100
Compilator cpp Status done
Runda Arhiva de probleme Marime 4.47 kb
#include <cstdint>
#include <cstddef>
#include <iostream>
#include <fstream>
#include <limits>

// ⚛ Rezolvare:
// ▮ 5, 3, 1⃞, 2, 4, ▮ 6⃞ , ...
// ▮ 4, 2, 1⃞, 3, ▮ 5⃞ , ...
// 
// Explicație:
// După ce fixăm valoarea 1 pe o poziție, o parte din valori sunt determinate,
// iar cele nedeterminate pot fi puse în corespondență cu o sub-problemă.
// 
// ▮ 1⃞, 2⃞, ...                → "+c[n-1]"
// ▮ 1⃞, 3, 2, 4⃞, ...          → "+c[n-3]"
// ▮ 1, 3, 5, 7, 8, 6, 4, 2   → "+1" (n par)
// ▮ 1, 3, 5, 7, 6, 4, 2      → "+1" (n impar)
// c[n] = c[n-1] + c[n-3] + 1.
// 
// Explicație:
// 
// Atunci cînd numărăm cele c[n] secvențe cu valoarea 1 pe poziția 1,
// valoarea 2 este
//    • pe poziția 1, în c[n-1] subsecvențe,
//    • pe poziția 3, în c[n-2] subsecvențe,
//    • pe poziția n, într-o singură subsecvență.

// ____________________________________________________________________________

namespace
{

// Spațiul de nume, anonim începe aici ...


bool
demo
()
{ bool  ok;
  
  ::std::ifstream  is("12perm.in");
  
  ::std::uint_least32_t  n;
  is >> n;
  ok = is.good();
  if( !ok)
  { ::std::cerr << "Citire eșuată." << ::std::endl;
    return false; // Eșec.
  }
  
  ::std::uint_least32_t  const min_n = 1;
  ::std::uint_least32_t  const max_n = 15000000;
  
  ok = ( (min_n <= n) && (max_n >= n)  );
  if( !ok)
  { ::std::cerr << "Intrare invalidă: n." << ::std::endl;
    return false; // Eșec.
  }
  
  ::std::uint_least32_t  max_v = 
    ::std::numeric_limits < ::std::uint_least32_t > :: max();
  ok = (max_n < max_v);
  if( !ok)
  { ::std::cerr << "Implementare incorectă." << ::std::endl;
    return false; // Eșec.
  }
  
  // Este adevărat că  (x % 2ⁿ) == (x & (2ⁿ-1)  ).
  // Și-ul pe biți este mai rapid decît aflarea restului după împărțire.
  ::std::uint_least32_t  const mask  = ((1<<20)-1);
  
  // c[n] ― numărul permutărilor π ∈ Sₙ, cu următoarele două proprietăți:
  //        • ∀ i ∈ ℤ  ( (2≤i) ∧ (i≤n)  ) ⇒ ( {1, 2} ∈ |π(i) - π(i-1)|  )
  //        • π(1) = 1.
  
  // x[n] ― numărul permutărilor π ∈ Sₙ, cu proprietatea:
  //        • ∀ i ∈ ℤ  ( (2≤i) ∧ (i≤n)  ) ⇒ ( {1, 2} ∈ |π(i) - π(i-1)|  )
  
  
  // Ne propunem să menținem următorul invariant:
  //   • u[t] == c[i+t], t←0..4.
  ::std::uint_least32_t  i = 1;
  ::std::uint_least32_t  u[3] = {1, 1, 2};
  auto  getC =
    [&i, &u]
    ( ::std::uint_least32_t  const m
    )
    { ::std::uint_least32_t  r;
      while(i<m)
      { r = (1 + (u[3-1] + u[3-3])  );
        u[0] = u[1];
        u[1] = u[2];
        u[2] = (r & mask);
        ++i;
      }
      return u[0];
    }
    ;
  // Calculăm numărul x[n], în variabila s.
  ::std::uint_least32_t  s = 0;
  if(0 != (n%2)  )
  { // Numărăm cele două secvențe în care 1 este în mijloc.
    // De exemplu,
    //    n: 5  (n > 1)
    //   π₁: 4 2 1⃞ 3 5
    //   π₂: 5 3 1⃞ 2 4
    // , sau
    //    n: 1
    //    π: 1
    if(1<n)
    { s = (mask & (s + 2)  );
    }
    else
    { s = (mask & (s + 1)  );
    }
  }
  
  ::std::uint_least32_t  k;
  ::std::uint_least32_t  h;
  ::std::uint_least32_t  e;
  ::std::uint_least32_t  d;
  ::std::size_t  f;
  
  // 4: 0 1⃞ 2 3
  // 5: 0 1 2 3 4
  k = n/2;
  while(0<k)
  { --k;
    // Numărăm secvențele în care 1 este pe poziția k, k ≤ ⌊n/2⌋.
    // 
    // Exemplu:
    // k: 0
    // ▮ 1⃞ ... 
    // k: 3
    // ▮ 6 4 2 1⃞ 3 5 ▮ 7⃞ ...    → c[n-2k].
    // ▮ 7 5 3 1⃞ 2 4 6 ▮ 8⃞ ...  → c[n-(2k+1)]
    
    if(0==k)
    { e = getC(n);
    }
    else
    { e = 0;
      f = 2;
      h = (1+(2*k));
      while(0<f)
      { --f;
        d = (n<=h) ? 1 : getC(n-h);
        e = (e+d) & mask;
        --h;
      }
    }
    
    s = (s + e) & mask;
    
    // Apoi, există tot atâtea secvențe în care 1 este pe poziția n-k.
    s = (s + e) & mask;
  }
  
  // no-throw, no-fail
  ::std::ofstream  os("12perm.out");
  os << s << ::std::endl;
  
  ok = os.good();
  if( !ok)
  { ::std::cerr << "Scriere eșuată." << ::std::endl;
    return false; // Eșec.
  }
  
  return true; // Izbândă.
}

// ____________________________________________________________________________

}

// ... spațiu de nume, anonim se sfârși.

int
main
()
{ int  status;
  
  try
  { bool  const ok = demo();
    status = ( ok ? 0 : 1 );
  }
  catch ( ... )
  { status = 2;
  }
  
  return status;
}


// ____________________________________________________________________________