Sunteți pe pagina 1din 19

Tabra de pregtire a lotului de juniori Vaslui

2014

Suport de curs
Lot
Operaii pe bii naional
Juniori

Prof. Dana Lica

1
Operaii pe bii
1. Reprezentarea intern a numerelor ntregi

Reprezentarea n memorie a numerelor ntregi se face printr-o secven de cifre de 0 i 1.


Aceast secven poate avea o lungime de 8, 16 sau 32 de bii.
Forma de memorare a ntregilor se numete cod complementar. n funcie de lungimea
reprezentrii se stabilete domeniu valorilor care pot fi stocate.
Modului de reprezentare (n cod complementar) va fi prezentat n cele ce urmeaz, folosind
reprezentarea pe o lungime de 8 bii, valabil pentru tipul shortint n Pascal, respectiv char n C/C++.

3 5 8 7

7 6 5 4 3 2 1 0
bitdesemn:
0 ptrnrpozitiv
1 ptrnrnegativ

Se observ c numerotarea poziiilor se face de la dreapta la stnga (de la 0 la 7), poziia 7


fiind bit de semn (0 pentru numerele pozitive i 1 pentru numerele negative).
Rezult c doar 7 bii (poziiile 0-6) se folosesc pentru reprezentarea valorii absolute a numrului.
Numerele ntregi pozitive se convertesc n baza 2, i se face completarea cu cifre 0
nesemnificative, pn la completarea celor 7 bii.

Exemplu:
S determinm forma de reprezentare a numrului ntreg 5.
510 =1012
Vor fi necesare 4 cifre de 0 nesemnificative, pentru completarea primelor 7 biii, iar poziia 7 (bitul 8)
va fi 0 deoarece numrul este pozitiv.
Deci reprezentarea intern este urmtoarea:

3
0 5
0 80 70 0 1 0 1

7 6 5 4 3 2 1 0

Nu n acelai mod se face reprezentarea numerelor ntregi negative. Pentru aceasta este necesar
efectuarea urmtorilor pai:
1) determinarea reprezentrii interne a numrului ce repezint valoarea absolut a numrului iniial.
Acesta are bitul de semn egal cu 0.
2) se calculeaz complementul fa de 1 a reprezentrii obinute la pasul anterior (bitul 1 devine 0, iar
bitul 0 devine 1)
3) se adun 1 (adunarea se face n baza 2) la valoarea obinut.

Exemplu:
Pentru determinarea reprezentrii numrului -5 se procedeaz n felul urmtor:
Reprezentarea valorii absolute a numrului -5 este 00000101.Complementul fa de 1 este:
11111010
Numrul obinut dup adunarea cu 1 este :
11111011
Deci reprezentarea valorii ntregi -5 pe 8 bii este:

2
3
1 5
1 81 71 1 0 1 1

7 6 5 4 3 2 1 0

Dup cum observm bitul de semn este 1 ceea ce ne indic faptul c avem de a face cu un
numr negativ.

Putem trage concluzia c numerele ntregi care se poate reprezenta pe 8 bii sunt cuprinse ntre
100000002 i 011111112 , adic -12810, 12710.

Aa cum am spus i la nceputul cursului, numerele ntregi se pot reprezenta n cod


complementar, avnd la dispoziie 16, 32 sau chiar i 64 de bii. Mecanismul este acelai, ns
valorile numerelor cresc.

2. Operatori la nivel de bit

Operatorii pe bii se pot aplica datelor ce fac parte din tipurile ntregi. Operaiile se efectueaz asupra
biiilor din reprezentarea intern a numerelor.

Operatorul de negaie
not (Pascal) respectiv ~ (C/C++)

Este un operator unar care ntoarce numrul ntreg a crui reprezentare intern se obine din
reprezentarea intern a numrului iniial, prin complementarea fa de 1 a fiecrui bit(10 i 01).

Exemplu:
not 5=-6 ~ 5 == -6

Reprezentarea lui 5 este 00000101. Complementnd acest numr obin reprezentarea 11111010
care corespunde numrului ntreg - 6. S verificm:
6 se reprezint intern astfel:
62 = 00000110, complementnd obinem 11111001 i adunnd 1 (n baza 2) se obine n final
11111010 adic exact reprezentarea intern returnat a numrului returnat de operaia not 5
respectiv ~5.

Operatorul de conjuncie
and respectiv &

Este un operator binar care returneaz numrul ntreg a crui reprezentare intern se obine prin
conjuncia biilor care apar n reprezentarea intern a operanzilor. Conjuncia se face cu toate perechile
de bii situai pe aceeai poziie.

Exemplu:
5 and 3 = 1 5 & 3 == 1

S verificm:
Reprezentarea intern a lui 5 este 00000101, iar a lui 3 este 00000011.
00000101 and 00000101 &
00000011 00000011
00000001 00000001

3
Aceast reprezentare este dat de numrul ntreg 1.

Operatorul de disjuncie
or respectiv |

Este un operator binar care returneaz numrul ntreg a crui reprezentare intern se obine prin
disjuncia biilor care apar n reprezentarea intern a operanzilor. Disjuncia se face ntre biii situai pe
aceeai poziie.

Exemplu:
15 or 3=15 15 | 3==15

S verificm:
Reprezentarea intern a lui 15 este 00001111 iar a lui 3 este 00000011.
00001111 or 00001111 |
00000011 00000011
00001111 00001111
Aceast reprezentare este dat de numrul ntreg 15.

Operatorul sau exclusiv


xor respectiv ^

Este un operator binar care returneaz numrul ntreg a crui reprezentare intern se obine prin
operaia or exclusiv asupra biilor care apar n reprezenarea intern a operanzilor. Operaia se face ntre
biii situai pe aceeai poziie.

Exemplu:
15 xor 3=12 15 ^ 3==12

S verificm:
Reprezentarea intern a lui 15 este 00001111 iar a lui 3 este 00000011.
00001111 xor 00001111 ^
00000011 00000011
00001100 00001100

Aceast reprezentare este dat de numrul ntreg 12.

Operatorul shift left


shl respectiv <<

Este un operator binar care returneaz numrul ntreg a crui reprezentare este obinut din
reprezentarea intern a primului operand prin deplasare la stnga cu un numr de bii egal cu al doilea
operand.

Exemplu:
4 shl 2=16 4 << 2==16

S verificm:
Reprezentarea intern a lui 4 este 00000100. Prin deplasare la stnga cu doi bii se obine
00010000, care este reprezentarea intern a lui 16.
Acest operator poate fi folosit pentru calculul numerelor ntregi de forma 2n prin efectuarea operaiilor
de forma 1 shl n. (1<<n)

4
Operatorul shift right
shr respectiv >>

Este un operator binar care returneaz numrul ntreg a crui reprezentare este obinut din
reprezentarea intern a primului operand prin deplasare la dreapta cu un numr de bii egal cu al doilea
operand.
Prin deplasarea la dreapta, primii biii din reprezentarea intern a numrului (poziiile 1,2, .a.m.d.) se
pierd iar ultimii se completeaz cu zero.

Exemplu:
14 shr 2=3 14 >> 2==3

S verificm:
Reprezentara intern a numrului 14 este 000001110. Prin deplasare la dreapta cu doi bii a
reprezentrii lui 14 se obine 00000011, care este reprezentarea intern a lui 3.
Operaia n shr 1 (n>>1) este echivalent cu mprirea ntreag la 2.

3. Operaii la nivel de bii

Transformarea unui bit n 1

Pornim de la valoarea ntreag X=50. Reprezentarea acestuia pe 8 bii este 00110010.


Presupunem c dorim setarea bitului 2 la valoarea 1. Pentru aceasta vom folosi o masc logic n care
doar bitul 2 este 1 restul biilor fiind 0, adic M=00000100. Valoarea lui M este 1 shl 2 (22).
Operaia de disjuncie SAU aplicat asupra lui X i a lui M, conduce la obinerea rezultatului dorit.

X 00110010 OR X 00110010 |
M 00000100 M 00000100
Rez 00110110 Rez 00110110

Generaliznd, dac se dorete ca valorii X, s i se seteze la valoarea 1, bitul B (0B7), atunci


masca logic este 1 shl B.
X or (1 shl B) X | (1 << B)

Transformarea unui bit n 0

S lum ca exemplu X= 109, pentru a vedea cum se seteaz un bit la valoarea 0. Reprezentarea intern
a lui este 01101101. Se cere s se seteze bitul 5 la valoarea 0. De data aceasta masca va conine toi
biii de 1, excepie bitul 5. Aspura lui X i M vom aplica I logic.

X 01101101 AND X 01101101 &


M 11011111 M 11011111
Rez 01001101 Rez 01001101
Presupunem c dorim s setm la 0 valoarea bitului B(0B7). Pentru determinarea valorii mtii M,
plecm de la valoarea 255 din care scdem 1 shl B.
X and (255 (1 shl B)) X & (255 (1 << B))

Masca mai poate fi obinut i aplicnd sau exclusiv n locul scderii, 255 xor (1 shl B).
Rescriem rezultatul sub forma:
X and (255 xor(1 shl B)) X & (255 ^(1 << B))
5
Testarea valorii unui bit

Plecm de la valoarea X=47. Reprezentarea intern a lui este 00101111. Presupunem c dorim s
cunoatem valoarea bitului 3 i bitului 6. Vom folosi mtile M1=00001000 i M2=01000000.
Vom aplica de fiecare dat I logic ntre X i cele dou mti:

X 00101111 AND X 00101111 &


M1 00001000 M1 00001000
Rez 00001000 Rez 00001000
Respectiv
X 00101111 AND X 00101111 &
M2 01000000 M2 01000000
Rez 00000000 Rez 00000000

Generaliznd, testarea se va realiza prin :


X and (1 shl B) <> 0 X & (1 << B) != 0

Testarea valorilor ultimilor bii

Pornim de la valoarea ntreag X=50. Reprezentarea acestuia pe 8 bii este 00110010. Presupunem
c dorim s cunoatem restul la mprirea ntreag a lui X la 8, adic X mod 8 respectiv x%8.
Valoarea ultimilor 3 bii din reprezentarea intern a lui lui, reprezint tocmai acest rest. Pentru aceasta
vom folosi o masc n care ultimii trei biii sunt 1 restul 0. Aceasta masc are valoarea 7, adic
00000111. Vom aplica operaia I logic.

X 00110010 AND X 00110010 &


M 00000111 M 00000111
Rez 00000010 Rez 00000010

Pe caz general, dac dorim s cunoatem valoarea ultimilor B bii(care este egal cu restul mpririi lui
X la 2B) vom exprima astfel:

X and (1 shl B 1) X & (1 << B 1)

6
Aplicaii
Problema 1.

Realizai un subprogram care determin numrul de cifre de 1 din reprezentarea binar a unui
numr natural nenul n mai mic ca 2000000000.

Soluia 1:
Numrul l vom reine ntr-o variabil longint respectiv int i vom parcurge secvenial biii lui.
function nr( n:longint):byte; int nr (int n){
var nm,i:byte; int nm=0;
begin for ( int i=0; i<32; i++)
nm:=0; if (n & (1 << i)) nm++;
for i:=0 to 31 do return nm;
if n and (1 shl i) <>0 then }
inc(nm);
nr:=nm;
end;

Soluia 2:
O alt soluie, mai rapid este folosirea operaiei I logic ntre valorile lui n i a lui n-1.
Aceast operaie anuleaz cel mai nesemnificativ bit de 1 a lui n. S luam ca exemplu valoarea lui n:

n =(110011101000100)2 n =(110011101000100)2
n-1 =(110011101000011)2 n-1 =(110011101000011)2
n and (n-1) =(110011101000000)2 n & (n-1) =(110011101000000)2

De aici i ideea algoritmului urmtor:


function nr( n:longint):byte; int nr (int n){
var nm:byte; int nm=0;
begin do {
nm:=0; n &= n-1;
repeat nm++;}
n:=n and(n-1); inc(nm); while (n);
until n = 0; return nm;
nr:=nm; }
end;
Rezultatul este mai bun la cea de a doua metod deoarece execut un numr de pai egali cu numrul
de cifre de 1 din reprezentare.

Problema 2.

Realizai un subprogram care verific dac un numr natural nenul n este o putere a lui 2.
Soluie:
Dac n este o putere a lui 2, va avea o singur cifr de 1 n reprezentarea binar. Dac
ne bazm pe observaia de la problema anterioar, obinem:

function pow2( n:longint):boolean; int pow2 (int n)


begin {
pow2 := n and(n-1) = 0; return (n & (n-1)==0);
end; }

7
Problema 3.

Realizai un subprogram care identific cea mai mare putere a lui 2 care l divide pe n, numr
natural nenul.

Soluia 1:
Problema se reduce la determinarea celui mai nesemnificativ bit de 1 din reprezentarea binar a
lui n. Prima soluie caut poziia, pornind cu bitul 0.

function UB (n:longint):longint; int UB (int n){


var i:integer; int i=0;
begin while (!(n & (1<<i))) i++;
i:=0; return 1 << i ;
while n and (1 shl i)=0 do inc(i); }
UB := 1 shl i;
end;

Soluia 2
Se bazeaz pe observaia c n and (n-1) are ca rezultat numrul din care lipsete cel mai
nesemnificativ bit de 0. Atunci aplicnd XOR intre valoarea inial a lui n i ce a lui n and(n-1)
vom obine valoarea cerut.

Exemplu:
n =(101000100)2 n =(101000100)2
n and (n-1) =(101000000)2 n & (n-1) =(101000000)2
n xor(n and (n-1))=(000000100)2 n ^( n & (n-1))=(000000100)2

function UB ( n:longint):longint; int UB (int n){


begin
UB := n xor(n and(n-1)); return n^(n & (n-1)) ;
end; }

O alt variant este descris n continuare:

function UB( n:longint):longint; int UB(int n){


begin
UB := (n and(-n)); return (n & (-n)) ;
end; }

Problema 4.

Realizai un subprogram care identific numrul de ordine al celui mai semnificativ bit de 1 al
lui n numr natural nenul.

Soluie

S lum ca exemplu urmtorul ir de operaii:


n=(10000000)2 n=(10000000)2
n=n or (n shr 1) n=n | (n >> 1)
n=(11000000)2 n=(11000000)2
n=n or (n shr 2) n=n | (n >> 2)
n=(11110000)2 n=(11110000)2
n=n or (n shr 4) n=n | (n >> 4)
n=(11111111)2 n=(11111111)2
8
Se observ c aplicnd o secven asemntoare de instruciuni putem transforma un numr n n alt
numr n care numrul de bii de 1 este egal cu 1 + indexul celui mai semnificativ bit de 1.

function index( n:longint):byte; int index(int n){


begin n = n | (n >> 1);
n := n or (n shr 1); n = n | (n >> 2);
n := n or (n shr 2); n = n | (n >> 4);
n := n or (n shr 4); n = n | (n >> 8);
n := n or (n shr 8); n = n | (n >> 16);
n := n or (n shr 16); return nr(n) - 1;
index:= nr(n)-1; }
end;

Problema 5

Gigel trebuie s cumpere n medicamente, numerotate de la 1 la n. Doctorul i-a dat m reete de dou
tipuri, codificate cu numerele 1, 2 astfel:
1 - reet necompensat, adic preul medicamentelor de pe reet se achit integral de ctre
cumprtor;
2 - reet compensat 50%, adic preul medicamentelor nscrise pe reet se njumtete.
Se tie c pe reete nu exist un alt medicament dect cele numeroatete de la 1 la n i o reet nu
conine dou medicamente identice.
Dac o reet este folosit atunci se vor cumpra toate medicamentele nscrise pe ea.
Cerin
Scriei un program care s determine suma minim de bani necesar pentru a cumpra exact cte unul
din fiecare dintre cele n medicamente, folosindu-se de reetele avute la dispoziie.
Date de intrare
Fiierul de intrare reteta.in are urmtorul format :
pe prima linie sunt scrise numerele naturale n i m;
pe urmtoarele m linii sunt descrise cele m reete, cte o reet pe o linie. Linia care descrie o reet
conine tipul reetei (1 necompensat sau 2 compensat), urmat de un numr natural q
reprezentnd numrul de medicamente de pe reet, apoi de q numere distincte din mulimea {1, 2,
..., n} reprezentnd medicamentele nscrise pe acea reet;
pe ultima linie a fiierului de intrare sunt scrise n numere naturale separate prin cte un spaiu,
reprezentnd n ordinea de la 1 la n, preul medicamentelor.
Toate numerele de pe aceeai linie sunt separate prin cte un spaiu.
Date de ieire
Fiierul de ieire reteta.out va conine o singur linie pe care va fi scris un numr real cu o
singur zecimal, reprezentnd suma minim determinat.
Restricii
1 N 20
1 M 15
1 preul oricrui medicament 200; Pentru datele de test exist ntotdeauna soluie.
Exemplu
reteta.in reteta.out Explicaie
4 5 45.0 Soluia s-a obinut prin folosirea primei i celei de a patra reete.
2 1 3 O alt soluie, dar de cost mai mare, s-ar fi obinut dac se folosea reeta
2 2 2 3
1 1 1 a patra i cea de a cincea.
1 3 4 1 2
1 1 3
8 20 2 16
Timp maxim de execuie/test: 1 secund
9
Soluie
Problema admite o soluie de complexitate exponenial O(2m). Se genereaz fiecare
submulime a mulimii reetelor. Se identific acele submulimi pentru care medicamentele nscrise pe
reete includ toate cele n medicamente, fr s repete vreunul.

Se va pstra ca soluie submulimea de reete care are cost total minim. n continuare v este
prezentat o rezolvare folosind lucru pe bii.

O reet se va codifica cu ajutorului unui ntreg (longint-long). Fiecare bit x (0x19)va


codifica prezenta-absena medicamentului x+1 de pe reet. Cum exist m reete, vectorul L va
codifica prin elementul L[i] reeta i+1, 0 i m-1 .
Plecnd de la exemplu din enun, a doua reet ce vonine medicamentele 2 i 3 se va codifica prin
elementul L[1] i va acea valoarea 6.

3
5 8 7 1 1

31 ..6 5 4 3 2 1 0

n acelai mod, pentru generarea submulimilor de reete ce vor fi folosite, ne vom folosi de vectorii
caracteristici asociai acestora. Pentru aceasta vom utiliza reprezentarea binar a tuturor numerelor de
la 0 la 2m-1.
Dac variabila s are valoarea 11 atunci ea cuprinde prima, a doua i a patra reet. Medicamentele pe
care acestea le conin sunt codificate n L[0], L[1] i L[3].

3
5 8 7 1 1 1

31 ..6 5 4 3 2 1 0

Variabila u va reprezenta o masc logic care indic medicamentele prinse pe reetele ce aparin
submulimii curente.
const INF=2000000000; #include <stdio.h>
var L,T,C:array[0..20]of longint; #include <string.h>
P:array[0..20]of real;
n,m,i,j,k,nr_med,x,s,u:longint; int L[20],T[20],C[20],n,m,i,j;
ok:boolean; int k,nr_med,x,s,u,corect;
sum,min:real; double P[20],sum,min;
f:text;
begin int main (){

assign( f, 'reteta.in');reset(f); freopen ( "reteta.in" , "r" , stdin );


read(f,n,m); scanf ( "%d %d" , &n , &m );
for i:=0 to m-1 do begin for ( i=0 ; i<m ; i++ ) {
read(f, T[i] , nr_med ); scanf ( "%d %d" , &T[i] , &nr_med );
for j:=1 to nr_med do begin for ( j=0 ; j<nr_med ; j++ ) {
read(f, x); scanf ( "%d" , &x );
L[i]:=L[i] or (1 shl(x-1)); L[i] |= 1<<(x-1);
end; }
end; }
for i:=0 to n-1 do read(f, C[i]); for (i=0;i<n;i++) scanf ("%d",&C[i]);
close ( f ); fclose ( stdin );

for i:=0 to m-1 do begin for ( i=0 ; i<m ; i++ ) {


sum := 0; sum = 0;
10
for j:=0 to n-1 do for ( j=0 ; j<n ; j++ )
sum:=sum+((L[i] shr j) and 1)*C[j]; sum += ((L[i]>>j)&1) * C[j];
P[i]:=sum/T[i]; P[i]=sum/T[i];
end; }
int pp=0;

min:=INF; min=2000000000;
for s:=0 to 1 shl m -1 do begin for (s=0 ; s<=(1<<m)-1 ; s++)
sum:=0; u := 0; ok:=true; i:=0; {
while (i<m) and ok do begin sum=0; u = 0; corect=1;
if (s shr i) and 1 = 1 then for (i=0 ; i<m && corect ; i++ )
if (u and L[i])<>0 then if ((s>>i)&1)
ok:=false if (u&L[i]) corect=0;
else begin else
sum:=sum+P[i]; {
u:=u or L[i]; sum += P[i];
end; u |= L[i];
inc(i); }
end;
if ok and (sum<min) and if (corect && sum<min && u==(1<<n)-1)
( u=( 1 shl n)-1) then min:=sum; min=sum;
end; }

freopen ( "reteta.out","w",stdout);
assign(f,'reteta.out'); rewrite(f); if (min!=2000000000)
if (INF-min>0.1)then printf ( "%.1lf\n" , min );
writeln (f, min:0:1) else printf ( "imposibil\n" );
else fclose ( stdout );
writeln(f,'imposibil'); return 0;
close ( f ); }
end.

Problema 6 - Codul Gray


Se consider irul tuturor cuvintelor de lungime n formate din cifre binare. S se aranjeze
aceste cuvinte astfel nct oricare dou alturate s difere printr-o singur poziie.
Exemplu: Pentru n=2 irul conine:
00
10
11
01
Soluie
Dac asociem fiecrui element din irul soluie numerele de la 1 la 1 shl n (1 << n) se observ c pentru
un numr x bitul schimbat n elementul urmtor din irul Gray este egal cu 1 + puterea maxim lui 2, care l
divide pe x.
var a:Array[1..20] of integer; #include <stdio.h>
f,g:text; int a[21];
n,i,x,nr:longint; int n,i,x,nr;

procedure afis(nr:longint); void afis(int nr){


var i:longint; int i;
begin for (i=1;i<=n;i++)
for i:=1 to n do printf("%d",a[i]);
write(g,a[i]); printf(" ---> %ld", nr);
write(g,' ---> ',nr); printf("\n");
writeln(g); }
end;
int main(){
begin freopen("gray.in","r",stdin);
assign(f,'Gray.in'); reset(f); freopen("gray.out","w",stdout);
assign(g,'Gray.out'); rewrite(g);
readln(f,n); scanf("%ld",&n);
for i:=1 to (1 shl n) do begin
afis(i); for (i=1; i<= (1<<n); i++){
x:=i; nr:=1; afis(i);

11
while x and 1=0 do begin x=i; nr=1;
nr:=nr+1; while (!(x & 1)) {
x:=x shr 1; nr++;
end; x >>= 1;
a[nr]:=a[nr] xor 1; }
end; a[nr] ^= 1;
close(f); close(g); } fclose(stdin); fclose(stdout);
end. return 0;}

Problema 7
Se dau v1 , v2 ... vk numere prime k<=20. S se determine cte numere mai mici ca un N dat
(N<=2000000000) sunt divizibile cu cel putin unul din cele K numere prime.
Exemplu
Pentru n=20 , k=4 si irul 2, 3, 5, 7 se va afia 15.

Soluie:
Se foloseste principiul includerii si al excluderii. Astfel numrul de numere divizibile cu cel puin un
numr dintre cel K este egal cu:
Nr (V1,V 2,..Vk) Vi Vi *Vj ...(1) k V1*V 2 * ..Vk
1 i n 1 i j ni

var v,mask:array[0..20]of longint; #include <fstream.h>


f,g:text; int v[21],mask[21];
i,x,numar,nr,produs,actual,suma,k,n: int i,x,numar,nr,produs,actual,suma,k,n;
longint;
int main(){
begin ifstream f("P1.in");
assign(f,'P1.in'); reset(f); ofstream g("P1.out");
assign(g,'P1.out'); rewrite(g); f>>k>>n;
read(f,k,n); for(i=1;i<=k;i++)
for i:=1 to k do f>>v[i];
read(f,v[i]); suma=0;
suma:=0; produs=1; nr=0;
produs:=1; nr:=0; for (i=1; i< (1 << k);i++) {
for i:=1 to (1 shl k)-1 do begin x=i; numar=1;
x:=i; numar:=1; while ((x & 1)==0) {
while x and 1=0 do begin x >>= 1;
x:=x shr 1; numar++;
numar:=numar+1; }
end; if (mask[numar]==1) {
if mask[numar]=1 then begin mask[numar]=0;
mask[numar]:=0; nr--;
nr:=nr-1; produs /= v[numar];
produs:=produs div v[numar]; }
end else{
else begin mask[numar]=1;
mask[numar]:=1; nr++;
nr:=nr+1; produs *= v[numar];
produs:=produs * v[numar]; }
end; actual = n/produs;
actual:=n div produs; if ((nr & 1)==1)
if nr and 1=1 then suma +=actual;
suma:=suma + actual else
else suma -=actual;
suma:=suma - actual; }
end; g<<suma;
writeln(g,suma); f.close();g.close(); return 0;
close(f); close(g);end. }

12
2. Arbori indexati binar

Arborii indexai binar reprezint o structur de date care permite efectuarea urmtoarelor operaii n
complexitata O(log N) pentru un vector de N elemente:

ADD(x, y) adun la elementul de pe poziia x din vector, valoarea y;


SUM (x) calculeaz suma primelor x elemente din vector (se presupune c vectorul este indexat de la 1 la
N).

Operaiile descrise mai sus pot fi efectuate n complexitatea menionat folosind un arbore de intervale
(descris n capitolul anterior), dar folosirea unui arbore indexat binar ofer anumite avantaje:

implementarea este mai uoar;


se folosesc fix N locaii de memorie pentru stocarea arborelui;
operaiile se pot extinde uor pentru a rezolva aceeai problem n mai multe dimensiuni.

Din pcate, aceast structur de date nu ofer aceeai flexibilitate precum un arbore de intervale, existnd seturi
de operaii care pot fi efectuate n complexitate logaritmic folosind doar arbori de intervale.

Eficiena acestei structuri de date nu are legtur cu echilibrarea arborelui precum alte structuri de date
convenionale, ci din modul de indexare a elementelor, innd cont de reprezentarea binar (de unde i numele
de arbore indexat binar).

Ideea de baz este c, precum un numr n poate fi exprimat ca sum de puteri ale lui 2, aa i un interval
[1n] poate fi exprimat ca reuniunea unor subintervale de lungimi egale cu puteri ale lui 2.

Vom lua n continuare, ca exemplu, intervalul [111]:


11 = 23 + 21 + 20 = 1011(2).
Eliminnd cel mai puin semnificativ bit de 1 din reprezentarea lui 11 n baza 2, constatm c suma pentru
intervalul [111] poate fi calculat astfel:
[111] ( 11 = 1011(2) )= [110] + [1111]
[110] ( 10 = 1010(2) )= [18] + [910]
[18] ( 8 = 1000(2) )

Astfel, dac se asociaz fiecrui element x din arbore un interval [x-2k+1x], unde k reprezint
numrul zerourilor terminale din reprezentarea binar a lui x, fiecare interval de forma [1x] poate fi exprimat
ca reuniunea a cel mult log2 N intervale, folosind procedeul (descris mai sus) de eliminare succesiv a celui mai
nesemnificativ bit de 1. Avnd stabilit aceast structur, operaia SUM(x) se efectueaz nsumnd intervalele
din descompunerea lui [1x].

Considerm un arbore indexat binar cu 15 elemente


(distribuia elementelor este reprezentat n figura 15

alturat). Pentru a actualiza valoarea unui element x 14


13
(operaia ADAUG(x, y) ) se actualizeaz nti 12
11
intervalul asociat lui x ( [x-2k+1x] ) ct i restul 10
intervalelor care-l conin pe x. Acest lucru se 9

efectueaz adugnd succesiv cel mai nesemnificativ


8
7
bit de 1. Spre exemplu, pentru a actualiza elementul 6

de pe poziia 9, se modific urmtoarele elemente (se 5


4
consider c sunt 15 elemente): 3
2

9 ( 9 = 1001(2) ) - [99] 1

10 (10 = 1010(2) ) - [910]


12 (10 = 1100(2) ) - [912]
Pentru a determina n O(1) cel mai nesemnificativ bit de 1 al unui numr, se poate folosi expresia
(x and (-x)) (Pascal) / (x & (-x)) (C/C++).
13
Sau
(x and (x-1)) xor x (Pascal) / (x & (x-1)) ^ x (C/C++).

S luam ca exemplu un vector V cu 15 elemente, care sufer modificri succesive. Acestuia i asociem
un arbore indexat binar, reprezentat sub forma unui vector AIB n care elementele rein informaii referitoare la
intervale de elemente din V, dup cum urmeaz:

[1..1] [1..2] [3..3] [1..4] [5..5] [5..6] [7..7] [1..8] [9..9] [9..10] [11..11] [9..12] [13..13] [13..14] [15..15]
AIB[1] AIB[2] AIB[3] AIB[4] AIB[5] AIB[6] AIB[7] AIB[8] AIB[9] AIB[10] AIB[11] AIB[12] AIB[13] AIB[14] AIB[15]

Implementare functiilor ADD si SUM se poate realiza astfel:

1 ... ...
2 function UB(x:integer):integer; #define UB(x) (x&(-x))
3 begin UB:=(x and (-x)) end;
4 ...
5 procedure ADD(x,val:integer); int AIB[NMAX],V[NMAX];
6 var i,j:integer;
7 begin
8 i:=x; void ADD(int x,int val)
9 while i<=n do begin {
10 inc(AIB[i],val); int i;
11 inc(i,UB(i)); for(i=x;i<=N;i+=UB(i)) AIB[i]+=val;
12 end; }
13 end;
14
15 function SUM(x,y:integer):integer; int SUM(int x)
16 var i,j,S:integer; {
17 begin int i,S=0;
18 i:=x;
19 while i>0 do begin for(i=x;i>0;i-=UB(i)) S+=AIB[i];
20 inc(S,AIB[i]);
21 dec(i,UB(i)); return S;
22 end; }
23 SUM:=S;
24 end;

Dei aceast structur de date nu este la fel de flexibil ca un arbore de intervale, ea poate fi extins s
suporte i alte operaii:

SUMA(x, y) suma elementelor cu poziii ntre x i y: se poate rescrie ca suma elementelor din intervalul
[1..x] minus suma elementelor din intervalul [1..y-1];
MODIFIC(x, y) actualizeaz valoarea elementului x la y, dac aceasta era mai mic dect y;
MAXIM(x) determin maximul din primele x elemente.
Aceste operaii pot fi implementate nlocuind operaia de adunare ("+") cu cea de maxim.

ADAUG(x, y, z) adaug la toate elementele cu poziii ntre x i y valoarea z;


AFL(x) determin valoarea elementului de pe poziia x.

Aplicaii - Arbori indexai binar

Problema Inv (www.infoarena.ro)

Se d un ir S de lungime n cu numere ntregi. Numim o inversiune o pereche de indici (i, j) astfel


nct 1 i < j n i S[i] > S[j].

Cerin
S se determine cte inversiuni sunt n irul dat.

14
Date de intrare
Fiierul de intrare inv.in conine pe prima linie numrul natural N. Pe urmtoarea linie se
gsesc N numere ntregi, reprezentnd n ordine elementele irului S.

Date de ieire
n fiierul de ieire inv.out se va afia un singur numr, reprezentnd restul mpririi numrului de
inversiuni din ir la valoarea 9917.

Restricii
2 N 100 000
Exemplu
inv.in inv.out
5 4
3 4 1 2 5

Soluie:

Problema se rezolv plecnd de la numrul maxim de inversiuni dintr-un vector. irul S preluat din inv.in va
fi sortat cresctor, pentru fiecare element reindu-se i poziia iniial n S(normalizare). Din numrul maxim se
inversiuni se vor scade numrul de inversiuni ce nu s-au format, adic numrul de elemente mai mici dect cel
curent, situate naintea lui, n irul S.
ntr-un arbore indexat binar, elementul AIB[x] va reine numrul de elemente introduse pn n acel moment i
care sunt situate n irul S pe poziii ntre [x-2k+1,..,x]. Evident ele au valori mai mici dect elementul curent i
deci nu formeaz cu el inversiuni.

1 #include <cstdio>
2 #include <algorithm>
3 #include <vector>
4
5 #define ub(x) (x&(-x))
6
7 #define NMAX 100001
8 #define MOD 9917
9
10 using namespace std;
11
12 vector < pair< int,int > > V;
13 int A[NMAX];
14 int N,i,x,k;
15 long long sol;
16
17 void add(int x)
18 {
19 int i=0;
20
21 for (i=x;i<=N;i+=ub(i)) A[i]++;
22 }
23
24 int Sum(int x)
25 {
26 int i=0,S=0;
27
28 for (i=x;i>0;i-=ub(i)) S+=A[i];
29 return S;
30 }
31
32 int main()
33 {
34 freopen("inv.in","r",stdin);
35 freopen("inv.out","w",stdout);
36
37 scanf("%d",&N);
38
39 for (i=0;i<N;i++)

15
{
scanf("%d",&x);
V.push_back(make_pair(x,i+1));
}
sort(V.begin(),V.end());

sol=1LL*N*(N-1)/2;
for (i=0;i<N;i++)
{
x=V[i].second;
sol-=1LL*Sum(x);
add(x);
}

printf("%d\n",sol%MOD);
return 0;
}

Problema Schi (www.infoarena.ro)

La un concurs de schi, are loc o prob contra-cronometru, concurenii trebuind s termine traseul n cel
mai scurt timp cu putin. Acestia vor evolua consecutiv i, dup finish, li se va comunica locul ocupat n
clasamentul intermediar. Mai exact, dupa evolutia celui de-al p-lea concurent, acesta va ti locul su n
clasamentul format de primii p concurenti. Cunoscnd poziiile intermediare, se cere s se determine
clasamentul final al competiiei.

Date de intrare
Pe prima linie a fiierului de intrare schi.in se afl N, reprezentnd numrul de concureni aliniai la
startul concursului. Pe urmtoarele N linii se afl cte o valoare ntreag, indicnd locul ocupat de fiecare
concurent n clasamentul intermediar, actualizat dup evoluia sa.

Date de ieire
Fisierul de iesire schi.out va avea N linii. Pe linia i se va afia numrul de ordine al concurentului
care ocupa locul i n clasamentul final.

Restricii
1 N 30 000
Exemplu
inv.in inv.out
8 7
1 2
1 8
3 6
4 1
4 3
2 5
1 4
3

Soluie:

Pentru a rezolva problema vom reine, ntr-un arbore indexat binar numrul de poziii libere n clasamentul final.
Astfel, elementul AIB[x] va reine cte poziii libere exist ntre [x-2k+1,..,x]. Se va parcurge invers irul din
fiierul de intrare, i pentru fiecare valoare X[i], se va determina cea mai mica poziie x pentru care suma
elementelor din AIB pe intervalul [1..x] este egal cu X[i].
Poziia x este poziia din clasamentul final a concurentului cu mumrul de ordine i.

1 #include <cstdio>
2 #include <climits>
3 #define ub(x) (x&(-x))
4 #define NMAX 30001
5
6 using namespace std;
7

16
8 int A[NMAX],X[NMAX],sol[NMAX];
9 int N,i,x;
10
11 void Decrease(int x)
12 {
13 int i;
14 for(i=x;i<=N;i+=ub(i)) A[i]--;
15 }
16
17 int Sum(int poz)
18 {
19 int i=0,T=0;
20 for(i=poz; i>0; i-=ub(i)) T+=A[i];
21 return T;
22 }
23
24 int binary_search(int x)
25 {
26 int S=1,D=N,M=0,T=0,Minim=INT_MAX;
27 while(S<=D)
28 {
29 M=(S+D)/2;
30 T=Sum(M);
31 if (T==x && M<Minim) Minim=M;
32 else if (T>=x) D=M-1;
33 else S=M+1;
34 }
35 return Minim;
36 }
37
38 int main()
39 {
40 freopen("schi.in","r",stdin);
41 freopen("schi.out","w",stdout);
42
43 scanf("%d",&N);
44 for (i=1; i<=N; ++i) scanf("%d",&X[i]);
45 for (i=1; i<=N; ++i) A[i]=ub(i);
44
45 for (i=N; i>=1; --i)
46 {
47 x=binary_search(X[i]);
48 sol[x]=i;
49 Decrease(x);
50 }
51
52 for (i=1;i<=N;++i) printf("%d\n",sol[i]);
53
54 return 0;
55 }

Problema Order (www.infoarena.ro)

Se consider N copii aezai n cerc i numerotai de la 1 la N n sens trigonometric. Copiii joac urmtorul joc:
jocul ncepe de la primul copil (cel al crui numr de ordine este 1); la fiecare al i-lea pas al jocului se
numar i copii in sens trigonometric i este eliminat copilul la care se ajunge; la pasul urmtor numrtoarea
ncepe de la copilul care urmeaz dup cel eliminat.
Aadar, dac numrul copiilor este suficient de mare, la primul pas este eliminat al doilea copil, la al doilea pas
al patrulea, la al treilea pas al aptelea, apoi la al patrulea pas al unsprezecelea i aa mai departe.

Cerin
Va trebui s determinai ordinea n care vor fi eliminai copiii.

Date de intrare
Fiierul de intrare order.in conine pe prima linie un numar intreg N, care reprezint numrul de copii.

Date de ieire

17
Fiierul de ieire order.out trebuie s conin o singura linie pe care se vor afla N numere distincte cuprinse
ntre 1 i N care reprezint numerele de ordine ale copiilor n ordinea n care au fost eliminai.

Restricii
2 N 30 000

Exemplu
order.in order.out
6 2 4 1 3 5 6

Soluie:

n mod asemntor, n AIB vom reine la fiecare pas, copiii rmai n cerc. Pentru fiecare extragere se va
identifica, printr-o cutare binar, cel mai mic indice x din AIB, pentru care suma [1..x] este egal cu cel a
copilului ce urmeaz la extragere.

1 #include <cstdio>
2 #include <climits>
3 #define ub(x) (x&(-x))
4 #define NMAX 30001
5
6 using namespace std;
7
8 int A[NMAX],sol[NMAX];
9 int x,N,i,ind,P=2;
10
11 void add(int x,int val)
12 {
13 int i=0;
14 for(i=x;i<=N;i+=ub(i)) A[i]+=val;
15 }
16
17 int Sum(int x)
18 {
19 int i=0,S=0;
20 for(i=x;i>0;i-=ub(i)) S+=A[i];
21 return S;
22 }
23
24 int binary_search(int x)
25 {
26 int D=N,S=1,M=0,Sm=0,Minim=INT_MAX;
27
28 while(S<=D){
29 M=(S+D)/2;
30 Sm=Sum(M);
31 if( Sm==x && M<Minim) Minim=M;
32 else if (Sm>=x) D=M-1;
33 else S=M+1;
34 }
35 return Minim;
36 }
37
38 int mod(int x,int MOD)
39 {
40 if (!(x%MOD)) return MOD;
41
42 return x%MOD;
43 }
44
45 int main()
44 {
45 freopen("order.in","r",stdin);
46 freopen("order.out","w",stdout);
47 scanf("%d",&N);
48 for(i=1;i<=N;++i) add(i,1);
49 ind=N;
50

18
51 for(i=1;i<=N;++i)
52 {
53 --ind;
54
55 x=binary_search(P);
56 add(x,-1);
57
58 sol[i]=x;
59 P+=i;
60
61 if (ind!=0) P=mod(P,ind);
62 }
63
64 for(i=1;i<=N;++i) printf("%d ",sol[i]);
65 return 0;
66 }

Probleme suplimentare:
Pe campion.edu.ro
Concurs1:
Pe infoarena.ro:
Datorii
AIB
Ben
Evantai

19

S-ar putea să vă placă și