#include “raylib.h”
#include
#pragma region assets
int player_sprite[64*3][36*3] = {{0,0,1,0,0},
{0,1,1,1,0},
{1,1,2,1,1},
{1,1,2,1,1},
{0,3,0,3,0}};
Color player_colors7832 = {(Color){ 0, 0, 0, 0 },GRAY,BLUE,RED};
int gun_sprite[64*3][36*3] = {{0,3,0,0,0},
{0,3,2,0,0},
{0,3,2,0,0},
{0,3,1,0,0},
{0,0,1,1,0}};
Color gun_colors7832 = {(Color){ 0, 0, 0, 0 },(Color){ 102, 47, 49, 255 },(Color){ 94, 94, 94, 255 },(Color){ 105, 106, 106, 255 }};
#pragma endregion assets
enum{ //TODO: fix the code
None=0,
Track,
Forward
};
const int Width = 64*3;
const int Height = 36*3;
const int PixelSize = 4;
Color Render[Width][Height];
class Rendered{ public: Vector2 position; int width; int height; int sprite[64*3][36*3]; //TODO: i forgot and accidentally removed all color ): done hopefully Color colors7832; int color_count;
void DrawPixel(Vector2 lposition, Color color){ int x = int(lposition.x); int y = int(lposition.y); if(x < Width && y < Height && x >= 0 && y >= 0){ Render[x][y] = color; } } void DrawSprite(Vector2 parentPos = {0,0}){ for(int i = 0;i7832, int temp_color_count){ for(int i = 0;i<width;i++) for(int j = 0;j<height;j++) sprite[j][i] = temp_sprite[i][j]; for(int i = 0;i<temp_color_count;i++){ colors[i] = temp_color[i]; } } void setSize(Vector2 temp_size){ width = temp_size.x; height = temp_size.y; } void setPos(Vector2 temp_position){ position = temp_position; } Rendered(Vector2 tposition, Vector2 tsprite_size, int tsprite[64*3][36*3], Color tcolor7832, int tcolor_count){ setPos(tposition); setSize(tsprite_size); initSprite(tsprite,tcolor,tcolor_count); } Rendered(){}};
class Movable : public Rendered{ //TODO: properly do classes now that i have a pdf of them
public:
int speed;
int move_type;
bool bounded;
std::list children;
//void shoot(Entity shooter, ) //TODO gun class that extends entity, bullet class, shooting system with spread, direction, speed, nr. of projectiles
//——————————————————————————————————————————
// Program main entry point
//——————————————————————————————————————————
int main(void)
{
// Initialization
//———————————————————————————————————————————
#include
#include
#include
#include
using namespace std;
ifstream fin(“elevi.in”);
ofstream fout(“elevi.out”);
struct nod{
struct {;
char nume256;
char clasa4;
double medie;
}inf;
nod *urm;
}*p, *u;
void addFront(char nume[], char clasa[], double medie, nod *&p, nod *&u){
if(!p){
p = new nod;
strcpy((*p).inf.nume, nume);
strcpy((*p).inf.clasa, clasa);
(*p).inf.medie = medie;
(*p).urm = NULL;
u = p;
}
else{
nod *q = new nod;
strcpy((*q).inf.nume, nume);
strcpy((*q).inf.clasa, clasa);
(*q).inf.medie = medie;
(*q).urm = NULL;
(*u).urm = q;
u = q;
}
}
void read(int &n){ fin >> n; fin.get(); while(n—){ char nume50; fin >> nume; fin.get();
char clasa10; fin >> clasa; fin.get(); double medie; fin >> medie; fin.get(); addFront(nume, clasa, medie, p, u); } }void cerinta1(){
int k = 0;
for(nod *i = p; i; i = (*i).urm)
if((*i).inf.medie >= 9)k++;
fout << k;
}
void sterge(nod *&i){
if(p == i){
nod *aux = p;
p = (*p).urm;
delete aux;
}
else{
nod *aux = i->urm;
i->urm = i->urm->urm;
delete aux;
}
}
void cerinta2(){
for(nod *i = p; i && i->urm; i = (*i).urm)
if((double)(*(*i).urm).inf.medie < 5)
sterge(i);
if((*p).inf.medie < 5)
sterge(p);
}
void cerinta3(){
for(nod *i=p; i; i=(*i).urm)
fout << (*i).inf.nume << “ “ << (*i).inf.clasa << “ “ << (*i).inf.medie << “\n”;
}
void schimba(nod *&i, nod *&j){
swap(i->inf.medie, j->inf.medie);
swap(i->inf.clasa, j->inf.clasa);
swap(i->inf.nume, j->inf.nume);
}
void sortareList_lexicografic(nod *&p, nod*&u){ for(nod *i = p; (*i).urm; i = (*i).urm) for(nod *j = i->urm; j; j = (*j).urm){ char clasa14, clasa24; strcpy(clasa1, (*i).inf.clasa); strcpy(clasa2, (*j).inf.clasa);
if(strcmp(clasa1, clasa2) > 0) schimba(i, j); else if(strcmp(clasa1, clasa2) == 0 && strcmp(i->inf.medie, j->inf.medie) < 0){ swap(i->inf.medie, j->inf.medie); swap(i->inf.nume, j->inf.nume); } }}
void cerinta4(){
sortareList_lexicografic(p, u);
}
void display(){
for(nod *i=p; i; i=(*i).urm) fout << (*i).inf.nume << “ “ << (*i).inf.clasa << “ “ << (*i).inf.medie << “\n”; }int main()
{
int n;
read(n);
display();
Hey guys, did you know that in terms of male human and female Pokémon breeding, Vaporeon is the most compatible Pokémon for humans? Not only are they in the field egg group, which is mostly comprised of mammals, Vaporeon are an average of 3”03’ tall and 63.9 pounds, this means they’re large enough to be able handle human dicks, and with their impressive Base Stats for HP and access to Acid Armor, you can be rough with one. Due to their mostly water based biology, there’s no doubt in my mind that an aroused Vaporeon would be incredibly wet, so wet that you could easily have sex with one for hours without getting sore. They can also learn the moves Attract, Baby-Doll Eyes, Captivate, Charm, and Tail Whip, along with not having fur to hide nipples, so it’d be incredibly easy for one to get you in the mood. With their abilities Water Absorb and Hydration, they can easily recover from fatigue with enough water. No other Pokémon comes close to this level of compatibility. Also, fun fact, if you pull out enough, you can make your Vaporeon turn white. Vaporeon is literally built for human dick. Ungodly defense stat+high HP pool+Acid Armor means it can take cock all day, all shapes and sizes and still come for more.
imi plac copiii mici ℂℂℂℂℂℂ∅∅∅∅∅∅∅PPPPPPPP∤∤∤∤∤∤∤∤∤∤∤∤∤∤∤∤∤∤∤∤∤ ❤❤💔💔💔❣❣❣💕💖🖤💚💦💦💦💦💦💦💦✡✡✡✡🔯🔯✡🕎
Articolul de față prezintă două rezolvări alternative ale problemei \(\texttt{PrimeIntreEle1}\), de complexități \(\mathcal{O}(n \log n)\), una bazată pe formula de inversiune Möbius, iar cealaltă utilizând indicatorul lui Euler și proprietățile acestei funcții.
Să considerăm problema #PrimeIntreEle1 :
Se citește un număr natural \(n\), \(n > 1\). Să se determine câte perechi \((a, b)\), \(1 \leqslant a \leqslant b \leqslant n\) de numere naturale sunt prime între ele.
O primă soluție constă în numărarea efectivă a perechilor \((a, b)\) cu \(1 \leqslant a \leqslant b \leqslant n\) de numere naturale prime între ele. Această abordare are complexitatea \(\mathcal{O}(n^2 \log n)\), deoarece calculul celui mai mare divizor comun \(\mathrm{cmmdc}(a, b)\) necesită \(\mathcal{O}(\log n)\) pași (complexitatea algoritmului lui Euclid – pentru descrierea acestui algoritm, vezi [5]). Putem descrie cu ajutorul pseudocodului această idee:
───────────────────────────────
Soluție brute-force (\(\mathcal{O}(n^2 \log n)\)).
───────────────────────────────
\(cnt \gets 0\);
\(\texttt{pentru}\;\;a = \overline{1, n}\;\texttt{ execută}\)
│ \(\texttt{pentru}\;\;b = \overline{a, n}\;\texttt{execută}\)
│ │ \(\texttt{dacă}\;\;\text{cmmdc}(a, b) = 1\;\texttt{atunci}\)
│ │ │ \(cnt \gets cnt + 1\);
│ │ └■
│ └■
└■
\(\texttt{scrie } cnt\);
───────────────────────────────
Implementare:
#include <iostream>
using namespace std;
int Cmmdc(int x, int y)
{
int r;
while(y != 0)
{
r = x % y;
x = y;
y = r;
}
return x;
}
int NumarPerechi(int n)
{
int res = 0;
for(int a = 1; a <= n; a++)
for(int b = a; b <= n; b++)
if(Cmmdc(a, b) == 1)
res++;
return res;
}
int main()
{
int n;
cin >> n;
cout << NumarPerechi(n);
return 0;
}
Vom prezenta acum și o altă soluție, mult mai eficientă, bazată pe formula de inversiune a lui Möbius.
Fie \(N = |\{(a, b) \; | \; a, b \in \{1, 2, \ldots, n\}, \text{cmmdc}(a, b) = 1\}|\).
Răspunsul problemei va fi atunci \(\frac{N + 1}{2}\). Într-adevăr, perechea \((a, b)\) cu \(a < b\) va fi numărată de două ori: \((a, b)\) și \((b, a)\), excepție făcând perechea \((1, 1)\) (perechile \((a, a)\) nu vor fi numărate pentru \(a > 1\)).
Funcția Möbius, \(\mu : \mathbb{N}^{\star} \to \mathbb{Z}\) este definită astfel:
\[
\mu(n) =
\begin{cases}
1, \text{ dacă } n = 1 \\
0, \text{ dacă există un număr prim } p \text{ astfel încât } p^2 | n \\
(-1)^k, \text{ dacă } n = p_1 \cdot \ldots \cdot p_k, \text{ unde } p_1, \ldots, p_k \text{ sunt prime distincte}
\end{cases}
\]
De asemenea, definim și funcția unitate \(\varepsilon : \mathbb{N}^{\star} \to \mathbb{N}\),
\[
\varepsilon(n) =
\begin{cases}
1, \text{ dacă } n = 1 \\
0, \text{ altfel }
\end{cases}
\]
Definiție. O funcție \(f : \mathbb{N}^{\star} \to \mathbb{C}\) se numește funcție aritmetică.
Dacă \(f\), \(g\) sunt două funcții aritmetice, atunci \[ f(n) = \sum_{d | n} \mu(d) g\left(\frac{n}{d}\right) \Leftrightarrow g(n) = \sum_{d | n} f(d). \]
Pentru demonstrație, se poate consulta [2].
În particular, luând \(f = \mu\), avem \(\mu(n) = f(n) = \sum_{d | n} \mu(d) \varepsilon\left(\frac{n}{d}\right)\), deci \(g = \varepsilon\), i.e.
\begin{equation}
\sum_{d | n} \mu(d) = \varepsilon(n). \;\;\;\; (1)
\label{eq1}
\end{equation}
Revenind la problemă, avem:
\begin{align*}
N &= |\{(a, b) \; | \; a, b \in \{1, 2, \ldots, n\}, \text{cmmdc}(a, b) = 1\}| \\
&= \sum_{1 \leqslant a, b \leqslant n} \varepsilon(\text{cmmdc}(a, b)) \\
&= \sum_{1 \leqslant a \leqslant n} \sum_{1 \leqslant b \leqslant n} \varepsilon(\text{cmmdc}(a, b)).
\end{align*}
Punând \(n \to \text{cmmdc}(a, b)\) în relația \((1)\), obținem:
\begin{align*}
N &= \sum_{1 \leqslant a \leqslant n} \sum_{1 \leqslant b \leqslant n} \varepsilon(\text{cmmdc}(a, b)) \\
&= \sum_{1 \leqslant a \leqslant n} \sum_{1 \leqslant b \leqslant n} \sum_{d | \text{cmmdc}(a, b)} \mu(d)
\end{align*}
Folosim acum o proprietate cunoscută, anume că \(d | \text{cmmdc}(a, b)\) dacă și numai dacă \(d | a\) și \(d | b\). Deducem că:
\begin{align*}
N &= \sum_{1 \leqslant a \leqslant n} \sum_{1 \leqslant b \leqslant n} \sum_{\substack{d | a \\ d | b}} \mu(d)
\end{align*}
Schimbând ordinea de sumare,
\begin{align*}
N &= \sum_{1 \leqslant a \leqslant n} \sum_{1 \leqslant b \leqslant n} \sum_{\substack{d | a \\ d | b}} \mu(d) \\
&= \sum_{d = 1}^n \mu(d) \sum_{\substack{1 \leqslant a \leqslant n \\ d | a}} \sum_{\substack{1 \leqslant b \leqslant n \\ d | b}} 1 \\
&= \sum_{d = 1}^n \mu(d) \left(\sum_{\substack{1 \leqslant a \leqslant n \\ d | a}} 1\right)^2 \\
&= \sum_{d = 1}^n \mu(d) \left\lfloor\frac{n}{d}\right\rfloor^2.
\end{align*}
Acest lucru este posibil deoarece:
\[\{(a, b, d) | 1 \leqslant a \leqslant n, 1 \leqslant b \leqslant n, d | a, d | b\} = \{(a, b, d) | 1 \leqslant d \leqslant n, 1 \leqslant a \leqslant n, d | a, 1 \leqslant b \leqslant n, d | b\}.\]
De remarcat că am folosit că \[|\{a \;|\; 1 \leqslant a \leqslant n, d | a\}| = |\{k \cdot d \;|\; 1 \leqslant k \cdot d \leqslant n\}| = \left\lfloor\frac{n}{d}\right\rfloor\] pentru un număr \(d \in \{1, 2, \ldots, n\}\) fixat.
În consecință, numărul căutat este
\begin{equation}
N = \sum_{d = 1}^n \mu(d) \left\lfloor\frac{n}{d}\right\rfloor^2. \;\;\;\; (2)
\label{eq2}
\end{equation}
Ecuația \((2)\) ne oferă un mod de calcul pentru \(N\) în \(\mathcal{O}(n)\). În funcție de cum se precalculează funcția \(\mu\), complexitatea variază de la \(\mathcal{O}(n)\) până la \(\mathcal{O}(n \log n)\).
O variantă de calcul a funcției Möbius în timp \(\mathcal{O}(n \log n)\) se bazează pe faptul că \(\mu(1) = 1\) și pe relația \((1)\), ce poate fi rescrisă
\begin{equation}
\mu(n) = \varepsilon(n)\; – \sum_{\substack{d | n \\ d < n}} \mu(d).
\label{eq3}
\end{equation}
Astfel, obținem următoarea schemă pentru calculul funcției \(\mu\):
───────────────────────────────
Precalcularea funcției Möbius \(\mu\)
───────────────────────────────
\(\texttt{mobius}(1) \gets 1\);
\(\texttt{pentru}\;\;i = \overline{1, n}\;\texttt{execută}\)
│ \(\texttt{pentru}\;\;j = \overline{2i, n, i}\;\texttt{execută}\)
│ │ \(\texttt{mobius}(j) \gets \texttt{mobius}(j)\; – \;\texttt{mobius}(i)\);
│ └■
└■
───────────────────────────────
Pentru exemplificare, să luăm cazul \(n = 6\). Avem \(12\) perechi de numere prime între ele: \((1, 1), (1, 2), \ldots, (1, 6), (2, 3), (2, 5), (3, 4), (3, 5), (4, 5), (5, 6)\).
Într-adevăr, aplicând formula de mai sus,
\begin{align*}
N &= \sum_{d = 1}^{6} \mu(d) \cdot \left\lfloor\frac{6}{d}\right\rfloor^2 \\
&= \mu(1) \cdot \left\lfloor\frac{6}{1}\right\rfloor^2 + \mu(2) \cdot \left\lfloor\frac{6}{2}\right\rfloor^2 + \mu(3) \cdot \left\lfloor\frac{6}{3}\right\rfloor^2 + \mu(4) \cdot \left\lfloor\frac{6}{4}\right\rfloor^2 + \mu(5) \cdot \left\lfloor\frac{6}{5}\right\rfloor^2 + \mu(6) \cdot \left\lfloor\frac{6}{6}\right\rfloor^2 \\
&= 36 – 9 – 4 – 1 + 1 \\
&= 23,
\end{align*}
iar răspunsul este dat de \(\frac{N + 1}{2} = \frac{23 + 1}{2} = 12\). (Avem \(\mu(1) = \mu(6) = 1\), \(\mu(2) = \mu(3) = \mu(5) = -1\), \(\mu(4) = \mu(2^2) = 0\).)
Implementare:
#include <iostream>
using namespace std;
const int NMAX = 1000;
int mobius[NMAX + 1];
void Precalc()
{
mobius[1] = 1;
for(int i = 1; i <= NMAX; i++)
for(int j = i << 1; j <= NMAX; j += i)
mobius[j] -= mobius[i];
}
int NumarPerechi(int n)
{
int res = 0;
for(int d = 1; d <= n; d++)
res += mobius[d] * (n / d) * (n / d);
return (res + 1) >> 1;
}
int main()
{
Precalc();
int n;
cin >> n;
cout << NumarPerechi(n);
return 0;
}
Pentru această problemă, mai există încă o soluție în care intervine indicatorul lui Euler. Pentru a calcula \(N := |\{ (a, b) | 1 \leqslant a \leqslant b \leqslant n, \text{cmmdc}(a, b) = 1 \}|\), putem fixa valoarea \(b \in \{1, 2, \ldots n\}\). Atunci:
\[ N = \sum_{b = 1}^{n} |\{ (a, b) | 1 \leqslant a \leqslant b, \text{cmmdc}(a, b) = 1\}|. \]
Fie \(\varphi : \mathbb{N}^{\star} \to \mathbb{N}^{\star}\), \(\varphi(n) = |\{x | 1 \leqslant x \leqslant n, \text{cmmdc}(x, n) = 1\}|\). Cu alte cuvinte, \(\varphi(n)\) reprezintă numărul de numere prime cu \(n\), mai mici sau egale cu \(n\). (Avem \(\varphi(1) = 1\).) Deducem că:
\[ N = \sum_{x = 1}^n \varphi(x) \]
Pentru precalcularea indicatorului lui Euler, vom folosi o proprietate datorată lui Gauss:
\[ \sum_{d | n} \varphi(d) = n \Leftrightarrow \varphi(n) = n \;-\; \sum_{\substack{d | n \\ d < n}} \varphi(d). \]
───────────────────────────────
Precalcularea funcției \(\varphi\)
───────────────────────────────
\(\texttt{phi}(1) \gets 1\);
\(\texttt{pentru } i = \overline{2, n} \texttt{ execută }\)
│ \(\texttt{phi}(i) \gets i \;-\; 1\);
└■
\(\texttt{pentru } i = \overline{2, n} \texttt{ execută } \)
│ \(\texttt{pentru } j = \overline{2i, n, i} \texttt{ execută }\)
│ │ \(\texttt{phi}(j) \gets \texttt{phi}(j) \;-\; \texttt{phi}(i)\);
│ └■
└■
───────────────────────────────
Complexitatea este tot \(\mathcal{O}(n \log n)\).
Remarcă: Pentru precalcularea funcțiilor \(\mu\) și \(\varphi\), complexitatea este \(\mathcal{O}(n \log n)\), deoarece, pentru \(i \in \{2, 3, \ldots, n\}\), \(j\) poate lua cel mult \(\frac{n}{i}\) valori. În total, vor fi efectuate maxim \(\displaystyle{\sum_{k = 1}^n} \frac{n}{k} = n \cdot H_n\) operații, unde \(H_n = \displaystyle{\sum_{k = 1}^n \frac{1}{k}}\).
Observație. Avem \(H_n = \Theta(\log n)\).
Demonstrație. Vom arăta că are loc următoarea inegalitate: \[\frac{n}{2} + 1 \leqslant H_{2^n} < n + 1, \text{pentru orice } n \in \mathbb{N^{\star}}.\]
Într-adevăr, să observăm că: \[\{1, 2, \ldots, 2^n\} = [1, 2^n] \cap \mathbb{N^{\star}} = \{1\} \cup \bigcup_{k = 1}^n \{2^{k – 1} + 1, \ldots, 2^k\}.\]
De aici rezultă \[H_{2^n} = 1 + \sum_{k = 1}^n \sum_{2^{k – 1} < j \leqslant 2^k} \frac{1}{j}.\]
Dar \(2^{k – 1} < j \leqslant 2^k \Leftrightarrow \frac{1}{2^k} \leqslant \frac{1}{j} < \frac{1}{2^{k – 1}}\) și, prin urmare,
\[\frac{1}{2} = \sum_{2^{k – 1} < j \leqslant 2^k} \frac{1}{2^k} \leqslant \sum_{2^{k – 1} < j \leqslant 2^k} \frac{1}{j} < \sum_{2^{k – 1} < j \leqslant 2^k} \frac{1}{2^{k – 1}} = 1.\]
În consecință, \[\frac{n}{2} = \sum_{k = 1}^n \frac{1}{2} \leqslant \sum_{k = 1}^n \sum_{2^{k – 1} < j \leqslant 2^k} \frac{1}{j} < \sum_{k = 1}^n 1 = n,\]
adică \(\frac{n}{2} + 1 \leqslant H_{2^n} < n + 1\), pentru orice \(n \in \mathbb{N^{\star}}\).
Totodată,
\begin{align*}
H_n &= \sum_{k = 1}^n \frac{1}{k} \\
&= \sum_{k = 1}^{n – 1} \frac{1}{k} + \frac{1}{n} \\
&= H_{n – 1} + \frac{1}{n}
\end{align*}
Din această egalitate reiese că \(H_n > H_{n – 1}\), pentru orice \(n \geqslant 2\), adică șirul \((H_n)_{n \geqslant 1}\) este monoton strict crescător.
Fie acum un număr natural \(n \in \mathbb{N^{\star}}\) arbitrar, fixat. Notăm \(k := \lfloor \log_2 n \rfloor\), deci \(k \leqslant \log_2 n < k + 1 \Leftrightarrow 2^k \leqslant n < 2^{k + 1}\).
Din monotonia șirului \((H_n)_{n \geqslant 1}\), obținem \(H_{2^k} \leqslant H_n < H_{2^{k + 1}}\). Conform inegalității precedente, deducem că \(H_{2^k} \geqslant \frac{k}{2} + 1\) și \(H_{2^{k + 1}} < k + 2\), deci \(\frac{k}{2} + 1 \leqslant H_n < k + 2\), i.e. \(H_n = \Theta(\log n)\).
Implementare:
#include <iostream>
using namespace std;
const int NMAX = 1000;
int phi[NMAX + 1];
void Precalc()
{
phi[1] = 1;
for(int i = 2; i <= NMAX; i++)
phi[i] = i - 1;
for(int i = 2; i <= NMAX; i++)
for(int j = i << 1; j <= NMAX; j += i)
phi[j] -= phi[i];
}
int NumarPerechi(int n)
{
int res = 0;
for(int i = 1; i <= n; i++)
res += phi[i];
return res;
}
int main()
{
Precalc();
int n;
cin >> n;
cout << NumarPerechi(n);
return 0;
}
O altă problemă în care se cere calcularea numărului de perechi de numere naturale mai mici decât un număr \(n\) este #divq .
Avem de răspuns la două întrebări (interogări), și anume:Pentru primul tip de întrebare, se observă că răspunsul căutat este chiar \(\tau(N)\), adică numărul divizorilor naturali ai lui \(N\). Deoarece avem \(Q \leqslant 10^6\) întrebări, va trebui să precalculăm, pentru toate numerele \(\leqslant \texttt{NMAX}\), numărul de divizori al acestora. Notăm cu \[\texttt{nrDiv}[n] = \tau(n) \text{ (numărul de divizori ai lui } n \text{)}.\]
Vom proceda asemănător cu algoritmul de la ciurul lui Eratostene. Parcurgem cu \(i\) toate numerele de la \(1\) la \(n\) și cu \(j\) toți multiplii lui \(i\) – de forma \(j = k \cdot i, k \in \mathbb{N}^{\star}\) – mai mici decât \(n\). Pentru fiecare astfel de număr \(j\), știm că \(i | j\), prin urmare incrementăm numărul de divizori al lui \(j\).
───────────────────────────────
Precalcularea funcției \(\tau\)
───────────────────────────────
\(\texttt{pentru } i = \overline{1, n} \texttt{ execută } \)
│ \(\texttt{pentru } j = \overline{i, n, i} \texttt{ execută }\)
│ │ \(\texttt{nrDiv}[j] \gets \texttt{nrDiv}[j] + 1\);
│ └■
└■
───────────────────────────────
Pentru cel de-al doilea tip de întrebare, numărul căutat este
\[\frac{N \cdot (N – 1)}{2} \;-\; \sum_{x = 1}^{N} \varphi(x) \;+\; 1.\]
Altfel spus, din numărul total de perechi de numere \((a, b)\), egal cu \(\binom{N}{2} = \frac{N \cdot (N – 1)}{2}\), scădem numărul tuturor perechilor coprime (vezi problema precedentă) și adăugăm \(1\) (deoarece nu ne interesează perechea \((1, 1)\) care ar fi contorizată prin \(\varphi(1)\) din suma de mai sus).
Codul sursă.
#include <fstream>
using namespace std;
ifstream f("divq.in");
ofstream g("divq.out");
const int NMAX = 1500000;
int nrDiv[NMAX + 1];
long long phi[NMAX + 1];
int Q, T, N;
void CalcNrDiv()
{
for(int i = 1; i <= NMAX; i++)
for(int j = i; j <= NMAX; j += i)
nrDiv[j]++; /// j = k * i este multiplu de i
}
void CalcPhi()
{
for(int i = 1; i <= NMAX; i++)
phi[i] = i;
for(int i = 1; i <= NMAX; i++)
for(int j = i << 1; j <= NMAX; j += i)
phi[j] -= phi[i];
for(int i = 1; i <= NMAX; i++)
phi[i] += phi[i - 1]; /// sume partiale
}
int main()
{
CalcNrDiv();
CalcPhi();
f >> Q;
while(Q--)
{
f >> T >> N;
if(T == 1)
g << nrDiv[N];
else
g << (long long)N * (N - 1) / 2 - phi[N] + 1;
g << '\n';
}
f.close();
g.close();
return 0;
}
[1] Math note — Möbius inversion
[2] Titu Andreescu, Dorin Andrica, NUMBER THEORY – Structures, Examples, and Problems
[3] Eratostene și alte ciururi
[4] Secvențe Farey
[5] CMMDC și CMMMC. Algoritmul lui Euclid
Se considera urmatoarea cerinta: avem un tablou unidimensional cu n elemente, asupra acestuia se fac doua tipuri de operatii:
1.Update(x, y), elementul de la pozitia x primeste valoarea y.
2.Suma[ST, DR], adica suma elementelor din a cu indicele i, ST≤i≤DR
Prima metoda, elementara, poate solutiona fiecare query de tip suma in O(n) timp si update in O(1) timp, parcurgand efectiv elementele din intervalul dat de query-ul SUMA.
O a doua metoda se bazeaza pe algoritmul de Square Root Decomposition (cunoscut si ca Smenul lui Batog, atat Infoarena, cat si GeeksForGeeks au articole ce explica rationamentul si implementarea). Astfel, complexitatea de timp scade la O(sqrt(n)).
Cea mai eficienta metoda este metoda prin care construim o structura de date arborescenta ce ne garanteaza ca orice operatie de tip update sau suma poate fi executata in O(log n). Modul in care construim acest arbore este urmatorul: la radacina avem intervalul [1, n], descendentii acestuia vor fi [1,n/2] si [n/2+1, n]. Vom mentine aceasta maniera recursiva pana cand ajungem la elemente individuale ale array-ului. Dupa construirea arborelui, trecem la operatia de update. Ideea este ca nu dorim sa actualizam toate nodurile, ci doar parintii nodurilor pana cand ajungem la radacina, astfel operatia va fi logaritmica. Pentru suma ideea este similara: noi impartim recursiv intervalul de cautare in jumatati pana cand gasim un nod ce contine intervalul cautat.
In concluzie, arborii de intervale sunt structuri de date arborescente ce permit rezolvarea eficienta a problemelor unde exista update-uri si query-uri pe intervale. Acestia pot fi implementati intr-o multitudine de probleme din concursuri.
Mai jos va las si implementarea mea a arborilor de intervale:
#include <bits/stdc++.h>
using namespace std;
int v[100], a[401];
void build (int st, int dr, int node) {
if (st == dr) {
a[node] = v[st];
}
else {
int mij = (st + dr) / 2;
build (st, mij, node * 2);
build (mij + 1, dr, node * 2 + 1);
a[node] = a[ node * 2 ] + a[ node * 2 + 1 ];
}
}
void update (int st, int dr, int node, int pos, int val) {
if (st == dr) {
a[node] = val;
v[pos] = val;
}
else {
int mij = (st + dr) / 2;
if (pos <= mij) {
update (st, mij, node * 2, pos, val);
}
else {
update (mij + 1, dr, node * 2 + 1, pos, val);
}
a[node] = a[node * 2 + 1] + a[node * 2];
}
}
int sum (int st, int dr, int node, int target_st, int target_dr) {
if (st>target_dr or dr<target_st) {
return 0;
}
else {
if (target_st <= st and dr <= target_dr) {
return a[node];
}
int mij = (st + dr) / 2;
return sum (st, mij, node * 2, target_st, target_dr) + sum (mij + 1, dr, node * 2 + 1, target_st, target_dr);
}
}
Sper ca v-am ajutat!