Автор проблеми: | De3b0o |
Підготовлено: | De3b0o |
Редаговано: | Hasan Saleh (MathModel) |
Рішення
(Ми будемо посилатися на поточне значення після всіх операцій як )
не може стати негативним у будь-який момент, тому що тоді як M-операції, так і S-операції зроблять його ще більш негативним, тому він ніколи не досягне . Це означає, що для того, щоб зробити рівним , спочатку ми повинні зробити його кратним (\text{cur}\mod z = 0), а потім продовжувати виконувати S-операції, поки не отримаємо .
Спочатку розглянемо випадки . Як ми вже сказали, не може бути негативним, тому якщо спочатку негативне, то відповідь .
Інший випадок - це якщо ми не можемо зробити кратним . Досить перевірити значення (n \cdot x^z \cdot y^z)\mod z, якщо воно не , то ми не можемо зробити кратним .
Доказ: Оскільки існує лише різних значень cur\mod z, множення більше ніж разів призведе нас до циклу.
Тепер, коли ми закінчили з випадками , ми завжди матимемо відповідь.
Ми чітко бачимо, що в оптимальному рішенні ми ніколи не будемо виконувати M-операцію, якщо \text{cur} < z є істинним, тому що якщо \text{cur} \ge z, ми можемо продовжувати виконувати S-операції, поки \text{cur} < z, це тому, що \text{cur}\mod z = (\text{cur}-z)\mod z, і оскільки невиконання S-операцій перед M-операцією вимагатиме від нас виконати більше S-операцій у майбутньому, що явно не є оптимальним.
Розглянемо випадок, коли x=y:
Твердження: Ми не будемо виконувати більше ніж M-операцій.
Доказ: Щоб було кратним , повинно мати всі прості множники . Коли ми виконуємо M-операцію, ми додаємо прості множники до , і максимальна різниця між кількістю появ простого множника в і кількістю появ того ж простого множника в становить , тому нам не потрібно більше ніж M-операцій, щоб зробити мати всі прості множники .
Це означає, що для x=y ми можемо просто продовжувати виконувати M-операції, поки \text{cur} < z, і якщо коли-небудь стане більшим за , ми можемо зробити рівним \text{cur}\mod z з вартістю \left \lfloor{\frac{\text{cur}}{z}} \right \rfloor (повторно виконуючи S-операції), ми зупиняємося, коли \text{cur}=0.
Тепер, щоб вирішити повну задачу, давайте спочатку визначимо стан () як стан, де ми помножили разів на і разів на , і повинно дорівнювати (n \cdot x^i \cdot y^j)\mod z (ми будемо посилатися на значення у цьому стані як \text{cur}[i][j]).
Ми можемо використовувати підхід динамічного програмування:
\text{dp}[i][j] дорівнює мінімальній можливій вартості для досягнення стану ()
Базовий випадок:
Перехід:
Відповідь - це мінімальне значення \text{dp}[i][j], де \text{cur}[i][j] = 0.
Тепер, оскільки ми не будемо виконувати більше ніж M-операцій, складність для dp становить \mathcal{O}(z^2), що є занадто великим.
Як ми вже сказали в рішенні для x=y, нам не потрібно більше ніж M-операцій, щоб зробити кратним . Це означає, що ми завжди можемо отримати відповідь менше ніж 10^{11}. Оскільки максимальна відповідь, яку ми могли б отримати, якщо ми виконали M-операції довільно, становить \log(z) \cdot \left \lfloor{\frac{\max(x,y) \cdot (z-1)}{z}}\right \rfloor + \log(z) \cdot (\log(z)+1), що строго менше ніж 10^{11}.
Оскільки відповідь завжди менше ніж 10^{11}, ми не будемо виконувати більше ніж 5\cdot10^5 M-операцій, оскільки вартість їх самих була б більшою ніж 10^{11}.
Це оптимізує рішення dp до \mathcal{O}(10^{11}), зробивши 5 \cdot 10^5 межою для , але це все ще занадто повільно.
Давайте спостерігати за станом (), де обидва та строго більші за , гарантовано, що \text{cur}[i-1][j]=\text{cur}[i][j-1]=0, і оскільки \min(\text{dp}[i-1][j],\text{dp}[i][j-1]) \le \text{dp}[i][j], нам не важливо стан (), оскільки ми вже маємо кращу відповідь.
Тепер ми можемо виконати попередній dp двічі, перший раз з i \le \log(z) , j \le 5 \cdot 10^5, і другий раз з i \le 5 \cdot 10^5 , j \le \log(z).
Отже, загальна складність коду становить \mathcal{O}(5 \cdot 10^5 \cdot \log(z)), що достатньо швидко, щоб пройти.
#include<bits/stdc++.h>
#define ll long long
#define d3 ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define cans cout << ans << "\n";
using namespace std;
const ll N = 500000 , lg = 35;
ll n , x , y , z , ans = LLONG_MAX;
ll dp[lg][N];
ll md[2][N];
ll fp(ll a , ll b)
{
if(b==0)
return 1;
ll c = fp(a,b/2);
if(b%2)
return c*c%z*a%z;
else
return c*c%z;
}
ll Mcost(ll a)
{
return a*(a+1)/2;
}
void rec(ll a , ll b)
{
dp[0][0]=n/z;
md[0][0]=md[1][0]=1;
for(int i = 1 ; N>i ; i++)
{
md[0][i]=md[0][i-1]*a%z;
md[1][i]=md[1][i-1]*b%z;
}
for(int i = 1 ; lg>i ; i++)
{
ll p = n*md[0][i-1]%z;
p*=a;
dp[i][0]=dp[i-1][0]+p/z;
}
for(int i = 1 ; N>i ; i++)
{
ll p = n*md[1][i-1]%z;
p*=b;
dp[0][i]=dp[0][i-1]+p/z;
}
for(int i = 1 ; lg>i ; i++)
{
for(int j = 1 ; N>j ; j++)
{
ll p = n*md[0][i-1]%z*md[1][j]%z;
p*=a;
dp[i][j]=dp[i-1][j]+p/z;
p = n*md[0][i]%z*md[1][j-1]%z;
p*=b;
dp[i][j]=min(dp[i][j],dp[i][j-1]+p/z);
}
}
for(int i = 0 ; lg>i ; i++)
{
for(int j = 0 ; N>j ; j++)
{
if(n*md[0][i]%z*md[1][j]%z==0)
ans=min(ans,dp[i][j]+Mcost(i+j));
}
}
}
int main()
{
d3
cin >> n >> x >> y >> z;
ll e = n*fp(x,100)%z*fp(y,100)%z;
if(n<0||e!=0)
{
cout << -1;
return 0;
}
rec(x,y);
rec(y,x);
cans
}