This submission is migrated from previous version of oj.uz, which used different machine for grading. This submission may have different result if resubmitted.
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <cstring>
#include <chrono>
#include <cassert>
#include <bitset>
#include <stack>
#include <queue>
#include <iomanip>
#include <random>
#ifdef _MSC_VER
# include <intrin.h>
# define __builtin_popcount __popcnt
# define __builtin_popcountll __popcnt64
#endif
//#pragma GCC optimize("Ofast")
#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#define x first
#define y second
#define ld long double
#define ll long long
#define ull unsigned long long
#define us unsigned short
#define lsb(x) ((x) & (-(x)))
#define pii pair <int, int>
#define pll pair <ll, ll>
using namespace std;
mt19937 gen(time(0));
uniform_int_distribution <uint32_t> rng;
//ifstream in("test.in");
//ofstream out("test.out");
const int MOD = 998244353;
// the following namespace includes many useful things
// for solving linear recurrences
namespace recurrences {
// binary exponentiation
template <int MOD>
int lgput(int n, int p) {
int ans = 1, x = n;
while (p) {
if (p & 1)
ans = 1LL * ans * x % MOD;
x = 1LL * x * x % MOD;
p >>= 1;
}
return ans;
}
// modular integer class
template <int MOD>
struct Int {
int x;
Int() {
x = 0;
}
Int(int _x) {
if (_x < 0)
_x += MOD;
if (_x >= MOD)
_x -= MOD;
x = _x;
}
friend ostream& operator << (ostream& os, const Int& X) {
os << (false ? X.x - MOD : X.x);
return os;
}
Int operator + (const Int& other) const {
int val = x + other.x;
return (val >= MOD ? val - MOD : val);
}
Int operator += (const Int& other) {
return *this = *this + other;
}
Int operator - (const Int& other) const {
int val = x - other.x;
return (val < 0 ? val + MOD : val);
}
Int operator -= (const Int& other) {
return *this = *this - other;
}
Int operator * (const Int& other) const {
return 1LL * x * other.x % MOD;
}
Int operator *= (const Int& other) {
return *this = *this * other;
}
Int operator / (const Int& other) const {
return 1LL * x * other.inv() % MOD;
}
bool operator == (const Int& other) const {
return x == other.x;
}
bool operator != (const Int& other) const {
return x != other.x;
}
Int pow(int p) const {
return lgput<MOD>(x, p);
}
int inv() const {
return lgput<MOD>(x, MOD - 2);
}
};
const bool slow_mult = false;
int get_primitive_root(int p) {
vector<int> fact;
int phi = p - 1, n = phi;
for (int i = 2; i * i <= n; ++i)
if (n % i == 0) {
fact.push_back(i);
while (n % i == 0)
n /= i;
}
if (n > 1)
fact.push_back(n);
for (int res = 2; res <= p; ++res) {
bool ok = true;
for (size_t i = 0; i < fact.size() && ok; ++i)
ok &= lgput<MOD>(res, phi / fact[i]) != 1;
if (ok) {
return res;
}
}
return -1;
}
int primitive_root = get_primitive_root(MOD);
int get_smallest_power(int n) {
int p = 1;
while (p < n)
p <<= 1;
return p;
}
bool calcW = true;
Int<MOD> valw[30], invvalw[30];
// modular
template <typename T>
struct Poly {
vector <T> p;
Poly() {
p.clear();
}
Poly(vector <T> values) {
p = values;
}
Poly(int val) {
p = { val };
}
T& operator [] (int index) {
assert(index < (int)p.size());
return p[index];
}
void setDegree(int deg) {
p.resize(deg + 1);
}
int deg() const {
return p.size() - 1;
}
friend ostream& operator << (ostream& os, const Poly& P) {
for (auto& i : P.p)
os << i << " ";
return os;
}
bool operator == (const Poly& other) const {
if (deg() != other.deg())
return 0;
for (int i = 0; i <= deg(); i++) {
if (p[i] != other.p[i])
return 0;
}
return 1;
}
Poly operator + (const Poly& other) const {
Poly sum(p);
sum.setDegree(max(deg(), other.deg()));
for (int i = 0; i <= other.deg(); i++)
sum[i] += other.p[i];
return sum;
}
// add
Poly operator += (const Poly& other) {
return *this = *this + other;
}
Poly operator - (const Poly& other) const {
Poly dif(p);
dif.setDegree(max(deg(), other.deg()));
for (int i = 0; i <= other.deg(); i++)
dif[i] -= other.p[i];
return dif;
}
// substract
Poly operator -= (const Poly& other) {
return *this = *this - other;
}
// scalar multiplication
Poly operator * (const T& other) const {
Poly mult(*this);
for (auto& i : mult.p)
i *= other;
return mult;
}
// scalar multiplication
Poly operator *= (const T& other) {
return *this = *this * other;
}
// scalar division
Poly operator / (const T& other) const {
Poly mult(*this);
Int<MOD> val = other.inv();
for (auto& i : mult.p)
i *= val;
return mult;
}
// scalar division
Poly operator /= (const T& other) {
return *this = *this / other;
}
Poly fft(bool invert) {
Poly Ans(p);
Int<MOD> root(primitive_root);
if (calcW) {
calcW = false;
for (int i = 0; i < 30; i++) {
valw[i] = root.pow((MOD - 1) >> (i + 1));
invvalw[i] = valw[i].inv();
}
}
int ind = 0, n = deg();
for (int i = 1; i < n; i++) {
int b;
for (b = n / 2; ind & b; b >>= 1)
ind ^= b;
ind ^= b;
if (i < ind)
swap(Ans[i], Ans[ind]);
}
for (int l = 2, p = 0; l <= n; l <<= 1, p++) {
Int<MOD> bw(!invert ? valw[p] : invvalw[p]);
for (int i = 0; i < n; i += l) {
Int<MOD> w(1);
for (int j = 0; j < l / 2; j++) {
int i1 = i + j, i2 = i + j + l / 2;
Int<MOD> val1(Ans[i1]), val2(Ans[i2] * w);
Ans[i1] = val1 + val2;
Ans[i2] = val1 - val2;
w *= bw;
}
}
}
if (invert) {
Int<MOD> inv = Int<MOD>(n).inv();
for (int i = 0; i < n; i++)
Ans[i] *= inv;
}
return Ans;
}
// multiplies 2 polynomials
Poly operator * (const Poly& other) const {
if (!primitive_root) { // for small sizes, use naive multiplication
Poly mult;
mult.setDegree(deg() + other.deg());
for (int i = 0; i <= deg(); i++) {
for (int j = 0; j <= other.deg(); j++)
mult[i + j] += p[i] * other.p[j];
}
return mult;
}
Poly A(p), B(other.p);
int sz = max(get_smallest_power(A.deg() + 1), get_smallest_power(B.deg() + 1)) * 2;
A.setDegree(sz), B.setDegree(sz);
A = A.fft(0);
B = B.fft(0);
for (int i = 0; i < sz; i++)
A[i] *= B[i];
A = A.fft(1);
A.setDegree(deg() + other.deg());
return A;
}
// p mod q
Poly operator % (const Poly& other) const {
int d = deg() - other.deg();
if (d < 0)
return *this;
if (false) {
Poly R2(p);
for (int i = deg(); i >= other.deg(); i--) {
R2 -= (other * R2[i] / other.p[other.deg()]).shift(i - other.deg());
}
R2.setDegree(other.deg() - 1);
//return R;
}
Poly A, B = other;
for (int i = 0; i <= d; i++) {
A.p.push_back(p[deg() - i]);
}
for (int i = 0; i <= B.deg() / 2; i++)
swap(B[i], B[B.deg() - i]);
Poly C = A * B.inverse(d);
C.setDegree(d);
for (int i = 0; i <= d / 2; i++)
swap(C[i], C[d - i]);
Poly R = *this - other * C;
R.setDegree(other.deg() - 1);
return R;
}
// *x^n
Poly shift(int index) {
Poly q = p;
q.setDegree(deg() + index);
for (int i = deg(); i >= 0; i--)
q[i + index] = q[i];
for (int i = index - 1; i >= 0; i--)
q[i] = T(0);
return q;
}
// derivate of P
Poly derivative() {
Poly D;
D.setDegree(deg() - 1);
for (int i = 1; i <= deg(); i++)
D[i - 1] = T(i) * p[i];
return D;
}
// integral of P
Poly integral() {
Poly I;
I.setDegree(deg() + 1);
for (int i = 0; i <= deg(); i++)
I[i + 1] = p[i] / T(i + 1);
return I;
}
// P^-1 mod x^n
Poly inverse(int n) {
Poly Inv(p[0].inv()), two(2);
int power = 1;
while ((power / 2) <= n) {
Poly A;
for (int i = 0; i <= power; i++)
A.p.push_back((i <= deg() ? p[i] : 0));
Inv = Inv * (two - A * Inv);
Inv.setDegree(power);
power <<= 1;
}
Inv.setDegree(n);
return Inv;
}
// log(P) mod x^n
Poly log(int n) {
Poly Log(derivative());
Log = Log * this->inverse(n);
return Log.integral();
}
// e^P mod x^n
Poly exp(int n) {
Poly Exp(1);
int power = 1;
while ((power / 2) <= n) {
Poly A(p);
A.setDegree(power);
Exp += Exp * A - Exp * Exp.log(power);
Exp.setDegree(power);
power <<= 1;
}
Exp.setDegree(n);
return Exp;
}
// p^power mod mod, where mod is a polynomial
Poly pow(uint64_t power, Poly mod) {
Poly Pow(1), X(p);
while (power) {
if (power & 1)
Pow = Pow * X % mod;
X = X * X % mod;
power >>= 1;
}
return Pow;
}
Poly pow(uint64_t power) {
Poly Pow(1), X(p);
while (power) {
if (power & 1)
Pow = Pow * X;
X = X * X;
power >>= 1;
}
return Pow;
}
};
// berlekamp-massey algorithm
template <int MOD>
Poly<Int<MOD>> berlekamp_massey(vector <Int<MOD>> values) {
Poly<Int<MOD>> P(values), B, C, Pol(-1);
int n = values.size(), lst = -1;
Int<MOD> lstError;
for (int i = 0; i < n; i++) {
Int<MOD> error = P[i];
for (int j = 1; j <= C.deg() + 1; j++)
error -= C[j - 1] * P[i - j];
if (error == Int<MOD>(0))
continue;
if (lst == -1) {
C = C.shift(i + 1);
lst = i;
lstError = P[i];
continue;
}
Poly<Int<MOD>> D = (Pol * error / lstError);
Poly<Int<MOD>> t = C;
// instead of shifting D with i - lst - 1 positions
// do the substraction on the last D.deg() coeficients
if (i - lst - 1 + D.deg() > C.deg())
C.setDegree(i - lst - 1 + D.deg());
for (int j = 0; j <= D.deg(); j++)
C[j + i - lst - 1] -= D[j];
if (i - t.deg() > lst - B.deg()) {
B = t;
Pol = B;
Pol = Pol.shift(1);
Pol[0] = Int<MOD>(-1);
lst = i;
lstError = error;
}
//C -= D;
}
return C;
}
// find kth term based on terms of recurrence
// assuming first term has index 1
template <int MOD>
Int<MOD> kth_term(vector <Int<MOD>> v, uint64_t k) {
vector <Int<MOD>> x = { Int<MOD>(0), Int<MOD>(1) };
Poly<Int<MOD>> CP = berlekamp_massey<MOD>(v);
Poly<Int<MOD>> X(x);
// characteristic polynomial is of form
// x^n - sigma(i = 1..n, c[i] * x^(n-i))
// that's why we need to reverse the recurrence
// from berlekamp-massey
CP *= Int<MOD>(-1);
CP = CP.shift(1);
for (int i = 0; i <= CP.deg() / 2; i++)
swap(CP[i], CP[CP.deg() - i]);
CP[CP.deg()] = 1;
X = X.pow(k - 1, CP);
Int<MOD> term(0);
for (int i = 0; i <= X.deg(); i++)
term += X[i] * v[i];
return term;
}
template <int MOD>
void berlekamp_massey_test() {
int n;
vector<Int<MOD>> v;
cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
v.push_back(x);
}
Poly<Int<MOD>> rec = berlekamp_massey<MOD>(v);
// sanity check
for (int k = rec.deg() + 2; k <= 30; k++) {
Int<MOD> val(0);
for (int i = k - rec.deg() - 1; i < k; i++)
val += v[i - 1] * rec[k - 1 - i];
assert(val == kth_term(v, k));
if (k > n)
v.push_back(val);
}
uint64_t k;
cin >> k;
cout << kth_term<MOD>(v, k) << "\n";
}
template <int MOD>
void berlekamp_massey_speed_test() {
int n;
vector<Int<MOD>> v;
n = 1000;
for (int i = 0; i < n; i++) {
int x;
//cin >> x;
x = rng(gen);
v.push_back(x);
}
ld t1 = clock();
for (int k = 1; k <= 200; k++)
kth_term<MOD>(v, k);
ld t2 = clock();
cout << (t2 - t1) / CLOCKS_PER_SEC << "s\n";
}
template <int MOD>
void inverse_test() {
int n;
cin >> n;
vector <Int<MOD>> v;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
v.push_back(Int<MOD>(x));
}
Poly<Int<MOD>> P(v), Inv, Exp, Log;
int deg = 10;
Inv = P.inverse(deg);
cout << "Inverse: " << Inv << "\n";
Exp = P.exp(deg);
cout << "Exponential: " << Exp << "\n";
Log = P.log(deg);
cout << "Logarithm: " << Log << '\n';
}
};
int T;
template <typename T>
T gcd(T a, T b) {
if (!a)
return b;
return gcd<T>(b % a, a);
}
using namespace recurrences;
ifstream in("popcorn.in");
ofstream out("popcorn.out");
const int K = (int)1e6 + 5;
struct Nod {
ll cost;
int a, b;
bool operator < (const Nod& other) const {
return cost > other.cost;
}
};
int n, q, x;
int v[5005];
priority_queue <Nod> pq;
ll dp[5005][5005];
bool check(int b, int c, int d) {
return min(v[c], v[d]) <= v[b] && v[b] <= max(v[c], v[d]);
}
void update(Nod nod) {
ll val = nod.cost;
int a = nod.a, b = nod.b;
if (val < dp[a][b]) {
dp[a][b] = val;
pq.push({ dp[a][b], a, b });
}
}
void solve(int test) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> v[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
dp[i][j] = (ll)1e18;
}
pq.push({ 0, 1, n - 1 });
while (!pq.empty()) {
Nod nod = pq.top();
pq.pop();
ll val = nod.cost;
int a = nod.a, b = nod.b;
if (val > dp[a][b])
continue;
if (a < n && b < n && check(a + 1, b, b + 1))
update({ val + abs(v[a] - v[a + 1]), a + 1, b });
if (a > 1 && b < n && check(a - 1, b, b + 1))
update({ val + abs(v[a] - v[a - 1]), a - 1, b });
if (b < n && a < n && check(b + 1, a, a + 1))
update({ val + abs(v[a] - v[b + 1]), b + 1, a });
if (b < n && a > 1 && check(b + 1, a - 1, a))
update({ val + abs(v[a] - v[b + 1]), b + 1, a - 1 });
if (a < n && check(b, a, a + 1))
update({ val + abs(v[a] - v[b]), b, a });
if (a > 1 && check(b, a - 1, a))
update({ val + abs(v[a] - v[b]), b, a - 1 });
}
ll ans = (ll)1e18;
for (int i = 1; i <= n; i++)
ans = min(ans, dp[i][i]);
cout << ans << "\n";
}
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
srand(time(0));
T = 1;
//cin >> T;
for (int t = 1; t <= T; t++) {
solve(t);
}
return 0;
}
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |