Basecamp
    Ana Səhifə
    Problemlər
    Müsabiqələr
    Kurslar
    Reytinq
    Postlar
    Mağaza
    Discord
Ağaclar
Daxil ol
medv
•Article•4 ay öncə

Ağaclar

Ağaclar alqoritmlərdə əsas verilən strukturlarından biridir və müxtəlif məsələlərdə tez-tez rast gəlinir.

Ağac — əlaqəli, dövrəsiz, istiqamətsiz qrafdır.

Bir G=(V,E) qrafı aşağıdakı xüsusiyyətləri ödəyirsə, ona ağac deyilir:

  • Əlaqəli — qrafdakı istənilən iki zirvə arasında yol mövcuddur.

  • Dövrəsiz — qraf heç bir dövr (cycle) ehtiva etmir.

n zirvəli ağacda həmişə dəqiq olaraq n−1 kənar (edge) olur.

Ağacın Əsas Xüsusiyyətləri

1. Əlaqəlilik və Yolların Təkliyi

Ağacda istənilən iki zirvə arasında unikal (tək) yol mövcuddur.

2. Kənarların Sayı

Hər hansı ağac üçün:

∣E∣=∣V∣−1

3. Dövr yoxdur

Ağaca əlavə edilən istənilən kənar mütləq dövr yaradır.

4. Kök (kök verilmiş ağac üçün)

Əgər bir zirvə kök kimi seçilərsə, ağac kökdən övladlara doğru istiqamətlənmiş olur. Bu, rekursiv keçidlərdə və alqoritmlərdə (DFS, LCA və s.) istifadə olunur.

5. Alt ağaclar

Hər hansı zirvə və onun nəslindən olan bütün zirvələr alt ağac yaradır. Alt ağac özü də ağacdır.

6. Yarpaqlar

Heç bir övladı olmayan (kök verilmiş ağacda) zirvələrə yarpaq deyilir.

7. Dərinlik və Hündürlük

  • Dərinlik — kökdən həmin zirvəyə olan məsafə.

  • Hündürlük — ağacdakı maksimum dərinlik.

8. Ağacın Mərkəzi

Mərkəz — bütün digər zirvələrə qədər olan maksimal məsafəni minimallaşdıran zirvə (və ya bitişik zirvələr cütü)dir.

Ağacın Alternativ Tərifləri

Ağac üçün aşağıdakı təriflər bir-birinə ekvivalentdir və müxtəlif kontekstlərdə istifadə oluna bilər:

  • Hər hansı bir kənarın silinməsi ilə qraf əlaqəsini itirən dövrəsiz qraf.

  • n zirvəsi və n−1 kənarı olan əlaqəli qraf.

  • n−1 kənarı olan və dəqiq bir komponentdən ibarət dövrəsiz qraf.

Ağacların Koddakı Təsviri

Məsələnin növündən və ağacın tipindən (ümumi, binar, kök verilmiş və s.) asılı olaraq müxtəlif verilən strukturlarından istifadə olunur:

1. Qonşuluq siyahısı (Adjacency Lists)

Bu yanaşma yarışma tipli məsələlərdə və istiqamətli/istiqamətsiz ağaclarla işləyərkən çox istifadə olunur.

int n;
vector<vector<int>> tree(n); // n zirvəli ağac

// u və v zirvələri arasında kənar əlavə etmək
tree[u].push_back(v);
tree[v].push_back(u); // istiqamətsiz ağac üçün

2. Valideyn massivi (Parent Array)

Çox yığcam təmsil formasıdır, xüsusilə ağac artıq qurulmuşdursa istifadə olunur.

vector<int> parent(n); // parent[i] --- i zirvəsinin valideyni
// əgər i kökdürsə, adətən parent[i] = -1 və ya 0 seçilir

3. Göstəricilər / Strukturlar (OOP üslubu)

Xüsusilə binar ağaclar, trie-lər, dekoratorlar qurarkən Python/Java/C++ dillərində obyekt-yönlü proqramlaşdırma ilə istifadə olunur.

  • Binar ağac:

struct Node 
{
  int val;
  Node* left;
  Node* right;
  Node(int v) : val(v), left(nullptr), right(nullptr) {}
};
C++
7 lines
115 bytes
  • Ümumi ağac:

struct Node 
{
  int val;
  vector<Node*> children;
  Node(int v) : val(v) {}
};
Eolymp #977. Bu, ağacdırmı?

İstiqamətsiz, özünə qayıdan və çoxlu kənarı olmayan bir qraf qonşuluq matrisası ilə verilir. Məlum etmək lazımdır ki, bu qraf ağacdırmı?

Giriş. Birinci sətdə n (1≤n≤100) — qrafın zirvələrinin sayı verilir. Sonra n×n ölçülü qonşuluq matrisası verilir. 1 — kənarın olduğunu, 0 isə olmadığını göstərir. Matris əsas diaqonala görə simmetrikdir.

Çıxış. Qraf ağacdırsa, "YES" çıxar, əks halda "NO" çıxar.

Nümunə giriş
3
0 1 0
1 0 1
0 1 0
Nümunə çıxış
YES
Məsələyə keçid
Həll

n zirvəli bir qrafın ağac olması üçün və yalnız o halda:

  1. Qraf bağlı olmalıdır. Birinci zirvədən başlayaraq dərinlikə görə axtarış (DFS) aparılır. Axtarış zamanı ziyarət edilmiş zirvələrin sayı sayılır. Əgər bu say n-ə bərabərdirsə, deməli qraf bağlıdır.

  2. ∣V∣=∣E∣+1. Əgər qraf ağacdırsa, o zaman n−1 kənara malikdir.

  3. Qraf sikl (dairəvi yol) içerməməlidir. Birinci zirvədən başlayaraq dərinlikə görə axtarış aparılır. Əgər geriyə kənar (back edge) tapılarsa, bu, qrafda siklin olduğunu göstərir və belə qraf ağac deyil.

1) və 2) şərtləri və ya 1) və 3) şərtləri ödənərsə, bu, qrafın ağac olduğunu təsdiqləmək üçün kifayətdir.

Nümunə

Nümunədə verilmiş qraf bir ağacdır.

Alqoritmin həyata keçirilməsi — 1) və 3) şərtlərinin yoxlanılması

Qrafın qonşuluq matrisası g və ziyarət statusunu saxlayan used massivini elan edək.

#define MAX 110
int g[MAX][MAX], used[MAX];

dfs funksiyası zirvə v-dən başlayaraq dərinlikə görə axtarış aparır. v zirvəsinin valideyni p-dir. Əgər sikl aşkar edilərsə, flag dəyişəni 1 edilir.

void dfs(int v, int p)
{

Əgər qraf artıq ağac deyilsə (flag=1), axtarışı davam etdirməyə ehtiyac yoxdur.

  if (flag) return;

Zirvə v ziyarət olunmuş kimi işarələnir.

  used[v] = 1;

Zirvə v ziyarət olunubsa, c dəyişəni 1 vahid artırılır.

  c++;

Kənar (v,i) geri kənar (back edge) olacaq və sikl yaradacaq, əgər i=p və i zirvəsi artıq ziyarət olunubsa (used[i]=1). Əgər sikl aşkar edilərsə, flag=1 təyin olunur. Əgər sikl yoxdursa, axtarış i zirvəsindən davam etdirilir.

  for(int i = 0; i < n; i++)
    if ((i != p) && g[v][i])
      if(used[i]) flag = 1; else dfs(i,v);
}

Proqramın əsas hissəsi. Giriş verilənlərini oxu.

scanf("%d",&n); 
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
  scanf("%d",&g[i][j]);

Dərinlikə görə axtarış zamanı ziyarət edilmiş zirvələrin sayı c dəyişənində saxlanılır. Əgər qrafda sikl yoxdursa, flag=0 olur. Əgər sikl aşkar edilərsə, flag=1 təyin olunur.

c = 0;
flag = 0;

Bütün zirvələr başlanğıcda ziyarət olunmamış kimi işarələnir (used massivini sıfırla).

memset(used,0,sizeof(used));

Dərinlikə görə axtarışı 0-cı zirvədən başlat. Bu zirvə ağacın köküdür və valideyni yoxdur. Ona görə də dfs funksiyasına ikinci arqument kimi −1 verilir.

dfs(0,-1);

Əgər flag=1 (sikl var) və ya c=n (qraf bağlı deyil) olarsa, bu, qrafın ağac olmadığını göstərir.

if (flag || (c != n)) printf("NO\n"); else printf("YES\n");

Python implementasiyası — 1) və 3) şərtlərin yoxlanılması

dfs funksiyası zirvə v-dən başlayaraq dərinlikə görə axtarış aparır. v zirvəsinin valideyni p-dir. Əgər sikl aşkar edilərsə, flag dəyişəni 1 edilir.

def dfs(v, p):

Funksiyada istifadə olunan qlobal dəyişənləri elan et.

  global flag, c

Əgər qraf artıq ağac deyilsə (flag=1), axtarışı davam etdirməyə ehtiyac yoxdur.

  if flag: return

Zirvə v ziyarət olunmuş kimi işarələnir.

  used[v] = 1

Zirvə v ziyarət olunub, c dəyişəni 1 artırılır.

  c += 1

Əgər i=p və i zirvəsi artıq ziyarət olunubsa (used[i]=1), onda (v,i) kənarı geri kənardır və sikl yaradır. Əgər sikl aşkar edilərsə, flag=1 qoyulur. Əks halda, axtarış i zirvəsindən davam etdirilir.

  for i in range(n):
    if i != p and g[v][i]:
      if used[i]: flag = 1
      else: dfs(i, v)

Proqramın əsas hissəsi. n dəyərini oxu.

n = int(input())

Dərinlikə görə axtarış zamanı ziyarət olunan zirvələrin sayı c dəyişənində saxlanılır. Əgər qrafda sikl yoxdursa, flag=0. Sikl aşkar edilərsə, flag=1 olur.

c = 0
flag = 0

Bütün zirvələr başlanğıcda ziyarət olunmamışdır (sıfırla başlanır).

used = [0] * n

Sıfırlarla dolu qonşuluq matrisi g yarat.

g = [[0] * n for _ in range(n)]

Qonşuluq matrisini oxu.

for i in range(n):
  g[i] = list(map(int, input().split()))

Dərinlikə görə axtarışı 0 zirvəsindən başlat. Bu zirvə ağacın kökü olduğuna görə onun valideyni yoxdur. dfs funksiyasına ikinci arqument olaraq −1 ötürülür.

dfs(0, -1)

Əgər geri kənar varsa (flag=1) və ya qraf əlaqəli deyilsə (c=n), o zaman qraf ağac deyil.

if flag or c != n:
  print("NO")
else:
  print("YES")
Alqoritmin implementasiyası — 1) və 2) şərtlərin yoxlanılması

Qrafın qonşuluq matrisini g və istifadə olunmuş zirvələri saxlayan used massivini elan et.

#define MAX 101
int g[MAX][MAX], used[MAX];

dfs funksiyası v zirvəsindən dərinlikə görə axtarış həyata keçirir.

void dfs(int v)
{

Zirvəni ziyarət olunmuş kimi işarələ.

  used[v] = 1;

Zirvə v ziyarət olundu, c dəyişənini 1 artır.

  c++;

v zirvəsindən keçid mümkün olan bütün i zirvələrini yoxla. v→i keçidi mümkündür əgər:

  1. (v,i) kənarı mövcuddur, yəni g[v][i]=1;

  2. i zirvəsi hələ ziyarət olunmayıb (used[i]=0)

Əgər hər iki şərt ödənilirsə, dərinlikə görə axtarışı i zirvəsindən davam etdir.

  for (int i = 1; i <= n; i++)
    if (g[v][i] && !used[i]) dfs(i);
}

Proqramın əsas hissəsi. n dəyərini oxu.

scanf("%d", &n); 

Qrafdakı kənarların sayını Edges dəyişənində saxla.

Edges = 0;

Bütün zirvələri başlanğıcda ziyarət olunmamış kimi işarələ (massivi sıfırla).

memset(used, 0, sizeof(used));

Qonşuluq matrisini g oxu. Kənarların sayını hesabla.

for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
{
  scanf("%d", &g[i][j]);
  Edges += g[i][j];
}

Qraf yönsüz olduğuna görə (u,v) və (v,u) kənarları eyni sayılır. Ona görə Edges dəyişənini 2-yə böl.

Edges /= 2;

Dərinlikə görə axtarış zamanı ziyarət olunan zirvələrin sayını c dəyişənində saxla.

c = 0;

Dərinlikə görə axtarışı 1-ci zirvədən başlat.

dfs(1);

Qraf ağacdır, əgər kənarların sayı n−1-ə bərabərdirsə və o, bağlıdırsa (c=n).

if ((Edges == n - 1) && (c == n)) printf("YES\n");
else printf("NO\n");

Python implementasiyası — 1) və 2) şərtlərinin yoxlanması

dfs funksiyası v zirvəsindən dərinliyə görə axtarışı həyata keçirir.

def dfs(v):
  global c

Zirvə v ziyarət olunduğu kimi işarələnir.

  used[v] = 1

Zirvə v ziyarət olunubsa, c dəyişəni 1 artırılır.

  c += 1

Zirvə v-dən çıxan keçidlər üzrə i zirvələrinə baxılır. v→i keçidi mümkündür, əgər:

  1. (v,i) kənarı mövcuddursa, yəni g[v][i]=1;

  2. i zirvəsi hələ ziyarət olunmayıbsa (used[i]=0).

Əgər hər iki şərt ödənirsə, axtarış i zirvəsindən davam etdirilir.

  for i in range(1, n + 1):
    if g[v][i] and not used[i]: dfs(i)

Proqramın əsas hissəsi. n dəyərini oxu.

n = int(input())

Qrafdakı kənarların sayını Edges dəyişənində saxla.

Edges = 0

Bütün zirvələr ilkin olaraq ziyarət olunmamış sayılır (massiv used sıfırlarla başlanır).

used = [0] * (n + 1)

Sıfırlarla doldurulmuş başlanğıc vəziyyətində qonşuluq matrisası g yaradılır.

g = [[0] * (n + 1) for _ in range(n + 1)]

Qonşuluq matrisası g oxunur. Qrafdakı kənarların sayı Edges dəyişənində toplanır.

for i in range(1, n + 1):
  g[i] = [0] + list(map(int, input().split()))
  Edges += sum(g[i])

Qraf istiqamətsiz olduğuna görə, (u,v) və (v,u) kənarları eyni sayılır. Bu səbəbdən Edges dəyişəninin dəyəri 2-yə bölünür.

Edges //= 2

Dərinlik üzrə axtarış zamanı ziyarət edilmiş zirvələrin sayı c dəyişənində saxlanılır.

c = 0

Dərinlik üzrə axtarışı 1-ci zirvədən başlat.

dfs(1)

Əgər qrafda n−1 kənar varsa və o əlaqəlidirsə (c=n), bu halda qraf ağacdır.

if Edges == n - 1 and c == n:
  print("YES")
else:
  print("NO")
Eolymp #978. Ağac əldə et

Döngüsüz və çoxlu kənarları olmayan əlaqəli istiqamətsiz qraf verilir. Qrafdan bəzi kənarları silmək icazəlidir. Məqsəd — nəticədə ağac əldə etməkdir.

Giriş. Birinci sətirdə iki tam ədəd — qrafın zirvələrinin sayı n (1≤n≤100) və kənarlarının sayı m verilir. Növbəti m sətirdə hər biri bir kənarı ifadə edən iki tam ədəd verilir. Qrafın əlaqəli olduğu zəmanət verilir.

Çıxış. n−1 ədəd kənar çap et — bu kənarlar ağacı təşkil etməlidir. Kənarların sırası önəmli deyil.

Nümunə giriş
4 4
1 2
2 3
3 4
4 1
Nümunə çıxış
1 2
2 3
3 4
Problemi aç
Həll

Birinci zirvədən başlayaraq dərinlik üzrə axtarış (DFS) aparın. Dərinlik üzrə axtarış ağacını qurun və bu ağacın bütün kənarlarını çap edin.

Nümunə

Nümunədə verilmiş qraf aşağıdakı kimidir:

Əgər dərinlik üzrə axtarış 1-ci zirvədən başladılarsa, keçilən kənarlar belə olacaq: (1,2),(2,3),(3,4).

Alqoritmin realizasiyası

Qrafın qonşuluq matrisi g massivində saxlanılır.

#define MAX 101
int g[MAX][MAX], used[MAX];

dfs funksiyası v zirvəsindən başlayaraq dərinlik üzrə axtarış aparır. Dərinlik üzrə axtarış ağacına daxil olan hər bir (v,i) kənarı çap olunur.

void dfs(int v)
{
  used[v] = 1;
  for (int i = 1; i <= n; i++)
    if (g[v][i] && !used[i])
    {
      printf("%d %d\n",v,i);
      dfs(i);
    }
}
C++
10 lines
150 bytes

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq.

scanf("%d %d",&n,&m);
memset(g,0,sizeof(g)); memset(used,0,sizeof(used));
while(m--)
{
  scanf("%d %d",&a,&b);
  g[a][b] = g[b][a] = 1;
}
C++
7 lines
138 bytes

Birinci zirvədən dərinlik üzrə axtarışa başlayın.

dfs(1);
Eolymp #11263. Cüt Ağac

Sizə tsiklsiz sadə qoşulmuş qraf – bir ağac verilir.

Tapşırıq ondan ibarətdir ki, maksimum neçə kənarı silmək olar ki, nəticədə alınan meşədə (forest) hər bir əlaqəli komponent cüt sayda zirvədən ibarət olsun.

Məsələn, aşağıda göstərilən 4 zirvəli ağacdan maksimum 1 kənar silmək olar ki, alınan meşədə bütün komponentlər cüt ölçülü olsun.

Giriş. Birinci sətrdə iki tam ədəd n (2≤n≤100, n cütdür) və m — ağacdakı zirvə və kənarların sayı verilir. Sonrakı m sətrin hər birində iki tam ədəd — ağacdakı kənarları təşkil edən zirvələr verilir. Ağacın kökü 1-ci zirvədir.

Çıxış. Cüt komponentlərdən ibarət meşə almaq üçün silinə biləcək maksimum kənarların sayını çap edin.

Nümunə giriş
10 9
2 1
3 1
4 3
5 2
6 1
7 2
8 6
9 8
10 8
10 lines
42 bytes
Nümunə çıxış
2
Problemi aç
Həll

Tapşırıq, ağacı cüt sayda zirvəsi olan əlaqəli komponentlərə bölmək və bu zaman silinə biləcək maksimum kənar sayını tapmaqdır.

DFS (dərinlik üzrə axtarış) alqoritmi ilə kök — 1-ci zirvədən başlayaraq hər bir v zirvəsi üçün onun alt ağacında neçə zirvə olduğunu hesablayırıq. Əgər bu say cütdürsə, v zirvəsini valideyninə birləşdirən kənar silinir.

Kök zirvə 1 üçün bütöv ağacın ölçüsü cütdür, lakin onu valideyninə bağlayan kənar yoxdur. Buna görə nəticədən 1 çıxmalıyıq.

Nümunə

Nümunədə verilmiş ağacı nəzərdən keçirək. Hər bir zirvənin yanında, həmin zirvənin kök olduğu alt ağacın ölçüsü qeyd olunub. Zirvələr 1,3 və 6-nın alt ağacları cüt sayda zirvəyə malikdir. Zirvə 1 kök olduğundan və onu valideynə birləşdirən kənar olmadığından, (1,3) və (1,6) kənarlarını silirik.

Ağac cüt sayda zirvəsi olan 3 əlaqəli komponentə bölünüb.

Alqoritmin reallaşdırılması

Massivləri elan edirik.

#define MAX 101
int g[MAX][MAX], used[MAX];

dfs funksiyası v zirvəsindən başlayaraq dərinlik üzrə axtarış aparır və v kök olduqda onun alt ağacındakı zirvələrin sayını qaytarır.

int dfs(int v)
{
  used[v] = 1;

s dəyişənində v zirvəsinin alt ağacındakı zirvələrin sayını hesablayırıq.

  int s = 1;

v zirvəsinin bütün qonşuları i üzərində iterasiya aparırıq. Əgər i zirvəsi istifadə olunmayıbsa, dfs(i) funksiyasını çağırıb nəticəni s-ə əlavə edirik.

  for (int i = 1; i <= n; i++)
    if (g[v][i] && !used[i]) s += dfs(i);

Əgər alt ağacın ölçüsü s cütdürsə, v və onun valideyni arasındakı kənar silinir.

  if (s % 2 == 0) res++;

v zirvəsinin kök olduğu alt ağacdakı zirvələrin sayını qaytar.

  return s;
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq və qrafı qururuq.

scanf("%d %d", &n, &m);
for (i = 0; i < m; i++)
{
  scanf("%d %d", &a, &b);
  g[a][b] = g[b][a] = 1;
}

Dərinlik üzrə axtarışı 1 zirvəsindən başlayırıq. res dəyişənində silinmiş kənarların sayını hesablayırıq.

res = 0;
dfs(1);

Çünki 1 zirvəsinin valideynə birləşdirən kənar yoxdur, nəticə olaraq res−1 çap olunur.

printf("%d\n", res - 1);

Python reallaşdırması

dfs funksiyası v zirvəsindən başlayaraq dərinlik üzrə axtarış aparır və v kök olduqda onun alt ağacındakı zirvələrin sayını qaytarır.

def dfs(v):
  used[v] = 1

s dəyişənində v zirvəsinin alt ağacındakı zirvələrin sayını hesablayırıq.

  s = 1

Uşaqlar i üzrə dövr qururuq. dfs(i) funksiyasının qaytardığı dəyəri s-ə əlavə edirik.

  for i in range(1, n + 1):
    if g[v][i] and not used[i]:
      s += dfs(i)

Əgər alt ağacın ölçüsü s cütdürsə, v ilə onun valideyni arasındakı kənarı silirik.

  if s % 2 == 0:
    global res
    res += 1

v kök olduqda alt ağacındakı zirvələrin sayını qaytarırıq.

  return s

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq və qrafı qururuq.

n, m = map(int, input().split())
g = [[0] * (n + 1) for _ in range(n + 1)]
used = [0] * (n + 1)

for _ in range(m):
  a, b = map(int, input().split())
  g[a][b] = g[b][a] = 1
Python
7 lines
175 bytes

1 zirvəsindən başlayaraq dərinlik üzrə axtarış aparırıq. res dəyişənində silinmiş kənarların sayını hesablayırıq.

res = 0
dfs(1)

Çünki 1 zirvəsinin valideynə birləşdirən kənarı yoxdur, nəticə olaraq res−1 çap olunur.

print(res - 1)
Eolymp #11429. Tabeçiliyində olan işçilər

Şirkətin strukturu verilir. Hər bir işçi üçün onun tabeliyində olan işçilərin sayını hesablayın.

Giriş. Birinci sətrdə n (1≤n≤2⋅105) — işçilərin sayı verilir. İşçilər 1, 2, ..., n kimi nömrələnib və 1-ci işçi şirkətin baş direktoru sayılır.

Sonrakı sətrdə n−1 tam ədəd verilir: 2-ci, 3-cü, ..., n-ci işçilərin hər biri üçün onların birbaşa rəhbərinin nömrəsi verilir.

Çıxış. n tam ədəd çap edin: 1, 2, ..., n-ci işçilərin hər biri üçün onun tabeliyində olan işçilərin sayı.

Nümunə giriş
5
1 1 2 3
Nümunə çıxış
4 1 1 0 0
Problemi aç
Həll izahı

Şirkətin strukturu ağac şəklində verilib. Ağacdakı hər bir v zirvəsi üçün sum[v] — v kök olan alt ağacda olan bütün zirvələrin sayını tapırıq. İşçi v üçün onun tabeliyində olan işçilərin sayı sum[v]−1 olacaq (çünki v özü öz tabeliyində sayılmır).

Dərinlik üzrə axtarışa 1-ci zirvədən — baş direktordan başlayırıq. Əgər v zirvəsinin övladları to1​,to2​,...,tok​-dırsa, onda

sum[v]=1+sum[to1​]+sum[to2​]+⋯+sum[tok​]

Əgər v zirvəsi ağacın yarpaq nöqtəsidirsə, onda sum[v]=1 təyin edilir.

Nümunə

Nümunədə göstərilən qrafda hər bir işçinin (zirvənin) qarşısında onun tabeliyində olan işçilərin sayı yazılıb.

Alqoritmin reallaşdırılması

Ağac qonşuluq siyahısı g ilə təqdim olunur.

vector<vector<int> > g;

dfs funksiyası v zirvəsindən dərinlik üzrə axtarış həyata keçirir və v zirvəsindən başlayan alt-ağacda olan qovşaqların sayını qaytarır. p — dərinlik üzrə axtarışda v zirvəsinin valideynidir.

int dfs(int v, int p)
{

İlkin olaraq, v zirvəsindən başlayan alt-ağac yalnız v qovşağını ehtiva edir.

  sum[v] = 1;

v→to qırağını nəzərdən keçirək. Əgər to=p-dirsə, onda sum[v] dəyərinə to zirvəsindən başlayan alt-ağacdakı qovşaqların sayını əlavə edirik.

  for (int to : g[v])
    if (to != p) sum[v] += dfs(to, v);

v zirvəsindən başlayan alt-ağacda olan qovşaqların sayını qaytar.

  return sum[v];
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxu və ağacı qur.

scanf("%d", &n);
g.resize(n + 1);

for (i = 2; i <= n; i++)
{
  scanf("%d", &x);

i işçisinin birbaşa rəhbəri x-dir.

  g[i].push_back(x);
  g[x].push_back(i);
}

Dərinlik üzrə axtarışı 1-ci zirvədən başlat.

sum.resize(n + 1);
dfs(1, -1);

Cavabı çap et. i işçisinin tabeliyində olanların sayı sum[i]−1-ə bərabərdir.

for (i = 1; i <= n; i++)
  printf("%d ", sum[i] - 1);
printf("\n");

Java dili ilə reallaşdırma


import java.util.*;

public class Main
{
  static ArrayList<Integer>[] g;	// ağacın siyahı ilə təsviri
  static int sum[];                 // hər bir işçi üçün alt-ağacdakı qovşaq sayı
  static int n;                     // işçilərin sayı
  
  static int dfs(int v, int p)
  {
    sum[v] = 1;
    for(int to : g[v])
      if (to != p) sum[v] += dfs(to, v);
    return sum[v];
  }
  
  @SuppressWarnings("unchecked")
  public static void main(String[] args)
  {
    Scanner con = new Scanner(System.in);
    n = con.nextInt();
    g = new ArrayList[n + 1]; // işçi nömrələri 1-dən başlayır
    sum = new int[n + 1];

    for (int i = 0; i <= n; i++)
      g[i] = new ArrayList<Integer>();

    for (int i = 2; i <= n; i++)
    {
      int x = con.nextInt();
      g[i].add(x);   
      g[x].add(i);   
    }
    
    dfs(1, -1); 
    for (int i = 1; i <= n; i++)
      System.out.print(sum[i] - 1 + " "); 
    System.out.println();
    con.close();
  }
}   
Java
42 lines
958 bytes

Python ilə reallaşdırma

Rekursiya üçün stekin ölçüsünü artırın.

import sys
sys.setrecursionlimit(1000000)

dfs funksiyası v qovşağından başlayaraq dərinlik-öncə axtarış aparır və v qovşağında kök salan alt-ağacdakı qovşaq sayını qaytarır. p isə v qovşağının valideynidir.

def dfs(v, p):

Əvvəlcə, v qovşağında kök salan alt-ağac yalnız özünü ehtiva edir.

  sum[v] = 1

v→to istiqamətli kənarını nəzərdən keçir. Əgər to=p-dirsə, to qovşağında kök salan alt-ağacdakı qovşaq sayını sum[v]-ə əlavə et.

  for to in g[v]:
    if to != p: sum[v] += dfs(to, v)

v qovşağında kök salan alt-ağacdakı qovşaq sayını qaytar.

  return sum[v]

Proqramın əsas hissəsi. İşçilərin sayını oxuyun.

n = int(input())

Qrafın qonşuluq siyahısını g kimi başladın.

g = [[] for _ in range(n+1)]

Giriş məlumatını oxuyun. Ağacı qurun.

lst = list(map(int, input().split()))
for i in range(2, n+1):
  a = lst[i-2]

i-ci işçi üçün (i≥2), onun birbaşa rəhbəri a-dır.

  g[a].append(i)
  g[i].append(a)

sum siyahısını başladın.

sum = [0] * (n + 1)

Dərinlik-öncə axtarışı 1 qovşağından başladın.

dfs(1, -1)

Cavabı çap edin. i-ci işçinin tabeliyində olanların sayı sum[i]−1-ə bərabərdir.

for i in range(1, n + 1):
  print(sum[i] - 1, end=" ")
Eolymp #11428. Ağacın Diametri

n qovşaqdan ibarət bir ağac verilir.

Ağacın diametri — iki qovşaq arasında olan maksimum məsafədir. Verilmiş ağacın diametrini tapın.

Giriş. Birinci sətirdə bir tam ədəd n (1≤n≤2⋅105) — ağacdakı qovşaqların sayı verilir. Qovşaqlar 1-dən n-ə qədər nömrələnib.

Sonrakı n−1 sətirin hər biri bir kənarı təsvir edir və a və b (1≤a,b≤n) ədədlərindən ibarətdir, bu da a və b qovşaqları arasında bir kənarın olduğunu bildirir.

Çıxış. Bir tam ədəd çap edin — ağacın diametri.

Nümunə giriş
5
1 2
1 3
3 4
3 5
Nümunə çıxışı
3
Tapşırığı aç
Həll

Hər hansı bir zirvədən, məsələn, 1-ci zirvədən başlayaraq dərinlik üzrə axtarış (depth-first search) et. 1-dən ən uzaqda yerləşən zirvəni tap və onu a kimi qeyd et. Sonra a zirvəsindən dərinlik üzrə axtarışa başla və a-dan ən uzaqda yerləşən zirvəni tap — onu b kimi qeyd edək. Zirvələr a və b arasındakı məsafə maksimal olacaq və bu məsafə ağacın diametrinə bərabərdir.

Nümunə

Nümunədəki ağacı nəzərdən keçirək.

Ağacın diametri 3-dür. Məsələn, maksimal məsafə 2 və 5 zirvələri arasında əldə edilir.

Tapşırıq

Ağacın diametrini tapın.

Alqoritmin reallaşdırılması

Daxil edilən ağacı qonşuluq siyahısı (g) kimi saxlayın. Mənbədən hər bir zirvəyə olan ən qısa məsafələri dist massivində saxlayın.

vector<vector<int>> g;
vector<int> dist;

dfs funksiyası zirvə v-dən dərinlik üzrə axtarış aparır. Mənbədən zirvə v-yə olan ən qısa məsafə d-dir. Zirvə v-nin dərinlik üzrə axtarışdakı valideyni p-dir.

void dfs(int v, int d, int p = -1)
{

Mənbədən zirvə v-yə olan ən qısa məsafəni dist[v]-də saxlayın.

  dist[v] = d;

Bütün (v,to) kənarlarını iterasiya edin. Əgər to zirvəsi v-nin valideyni deyilsə, to üçün dərinlik üzrə axtarış başladın. Mənbədən to-ya olan məsafə d+1 olacaq.

  for (int to : g[v])
    if (to != p) dfs(to, d + 1, v);
}

Proqramın əsas hissəsi. Giriş verilənlərini oxuyun.

scanf("%d", &n);
g.resize(n + 1);
dist.resize(n + 1);

Yönsüz ağacı qurun.

for (i = 1; i < n; i++)
{
  scanf("%d %d", &u, &v);
  g[u].push_back(v);
  g[v].push_back(u);
}

1-ci zirvədən bütün digər zirvələrə olan ən qısa məsafələri tapın. Bu zirvədən ən uzaq məsafədə olan zirvəni a kimi qeyd edin.

dfs(1, 0, -1);
a = max_element(dist.begin(), dist.begin() + n + 1) – 
                dist.begin();

a zirvəsindən bütün digər zirvələrə olan ən qısa məsafələri tapın. Bu zirvədən ən uzaqda olan zirvəni b kimi qeyd edin.

dfs(a, 0,  -1);
b = max_element(dist.begin(), dist.begin() + n + 1) – 
                dist.begin();

Ağacın diametrini çap edin — yəni a ilə b arasındakı ən qısa məsafəni.

printf("%d\n", dist[b]);
Eolymp #11430. Tree Distances I

n zirvədən ibarət bir ağac verilir.

Hər bir zirvə üçün digər zirvələrə olan maksimal məsafəni tapın.

Giriş. İlk sətirdə bir tam ədəd n (1≤n≤2⋅105) — ağacdakı zirvələrin sayı verilir. Zirvələr 1-dən n-ə kimi nömrələnmişdir.

Növbəti n−1 sətrdə ağacdakı kənarlar təsvir olunur: hər sətrdə iki tam ədəd a və b (1≤a,b≤n) verilir, bu da a və b zirvələri arasında bir kənar olduğunu göstərir.

Çıxış. n ədəd çap edin. Hər bir 1-dən n-ə qədər zirvə üçün, həmin zirvədən digər zirvələrə olan maksimal məsafəni çap edin.

Nümunə giriş
5
1 2
1 3
3 4
3 5
Nümunə çıxış
2 3 2 3 3
Problemi aç
Həll

İki dəfə dərinlik üzrə axtarış (DFS) etməklə ağacın diametrini tapırıq.

Ağac dövr olmayan birləşik qraf olduğuna görə, həm DFS, həm də BFS başlanğıc zirvədən bütün digər zirvələrə olan ən qısa məsafələri düzgün hesablayır.

a və b — bir-birindən ən uzaqda yerləşən iki zirvədir — yəni ağacın diametrini təyin edən zirvələrdir.

a zirvəsindən bütün digər zirvələrə qədər olan ən qısa məsafələri dista massivində, b zirvəsindən olan məsafələri isə distb massivində saxlayırıq.

Sonra, hər bir i zirvəsi üçün digər zirvələrə qədər maksimal məsafə belə hesablanır:

max(dista[i],distb[i])

Nümunə

Misaldakı ağacı nəzərdən keçirək.

Ağacın diametri, məsələn, 2 və 5 zirvələri arasındakı yol ola bilər.

Alqoritmin implementasiyası

Girişdə verilən ağac g qonşuluq siyahısı kimi saxlanılır.

a və b zirvələrindən olan ən qısa məsafələr müvafiq olaraq dista və distb massivlərində saxlanılır.

vector<vector<int>> g;
vector<int> dista, distb;

dfs funksiyası zirvə v-dən başlayaraq dərinlik üzrə axtarış aparır.

  • d parametri — mənbədən v zirvəsinə qədər olan məsafəni göstərir;

  • nəticələr dist massivində saxlanılır;

  • p parametri — axtarış zamanı v zirvəsinin valideyni.

void dfs(int v, int d, vector<int>& dist, int p = -1)
{

Mənbədən v zirvəsinə qədər olan məsafəni dist[v] dəyişənində saxla:

  dist[v] = d;

Bütün (v,to) kənarları üzrə dövr qur. Əgər to — v zirvəsinin valideyni deyilsə, o zaman həmin to zirvəsi üçün dərinlik üzrə axtarış davam etdir:

  for (int to : g[v])
    if (to != p) dfs(to, d + 1, dist, v);
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq.

scanf("%d", &n);
g.resize(n + 1);
dista.resize(n + 1);
distb.resize(n + 1);

Yönsüz ağacı qururuq.

for (i = 1; i < n; i++)
{
  scanf("%d %d", &u, &v);
  g[u].push_back(v);
  g[v].push_back(u);
}

Zirvə 1-dən ən qısa məsafələri hesablayırıq. Ondan ən uzaqda yerləşən zirvə a-dır.

dfs(1, 0, dista, -1);
a = max_element(dista.begin() + 1, dista.begin() + n + 1) - dista.begin();

Daha sonra, a zirvəsindən ən qısa məsafələri hesablayırıq və dista massivində saxlayırıq. a zirvəsindən ən uzaq olan zirvə b olacaq. a və b arasındakı məsafə ağacın diametridir.

dfs(a, 0, dista, -1);
b = max_element(dista.begin() + 1, dista.begin() + n + 1) - dista.begin();

Sonra, b zirvəsindən ən qısa məsafələri hesablayırıq və distb massivində saxlayırıq.

dfs(b, 0, distb, -1);

Hər bir i zirvəsi üçün ondan ən uzaq zirvəyə olan məsafəni çap edirik.

for (i = 1; i <= n; i++)
  printf("%d ", max(dista[i], distb[i]));
printf("\n");

Python ilə reallaşdırma

Rekursiya yığını üçün limit artırılır.

import sys
sys.setrecursionlimit(1000000)

dfs funksiyası zirvə v-dən başlayaraq dərinlik üzrə axtarış (DFS) yerinə yetirir.

  • d parametri başlanğıc nöqtədən v zirvəsinə olan ən qısa məsafəni göstərir.

  • Nəticələr dist massivində saxlanılır.

  • p parametri v zirvəsinin DFS zamanı valideynini göstərir.

def dfs(v, d, dist, p = -1):

Başlanğıc nöqtədən v zirvəsinə olan ən qısa məsafəni dist[v]-də saxla.

  dist[v] = d

Bütün (v,to) kənarlarını nəzərdən keçirt. Əgər to v-nin valideyni deyilsə, bu halda to zirvəsi üçün DFS çağır.

  for to in g[v]:
    if to != p:
      dfs(to, d + 1, dist, v)

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyun.

n = int(input())
g = [[] for _ in range(n + 1)]
dista = [0] * (n + 1)
distb = [0] * (n + 1)

Yönsüz ağacı qurun.

for _ in range(n - 1):
  u, v = map(int, input().split())
  g[u].append(v)
  g[v].append(u)

Ən qısa məsafələri 1-ci zirvədən hesablayın. Ondan ən uzaqda yerləşən zirvə a-dır.

dfs(1, 0, dista)
a = dista.index(max(dista[1:]))

Daha sonra a zirvəsindən ən qısa məsafələri hesablayın və nəticələri dista massivində saxlayın. a-dan ən uzaqda yerləşən zirvə b-dir. a və b zirvələri arasındakı məsafə ağacın diametri olacaq.

dfs(a, 0, dista)
b = dista.index(max(dista[1:]))

Daha sonra b zirvəsindən ən qısa məsafələri hesablayın və nəticələri distb massivində saxlayın.

dfs(b, 0, distb)

Hər bir i zirvəsi üçün ondan ən uzaq zirvəyə olan məsafəni çap edin.

result = [max(dista[i], distb[i]) for i in range(1, n + 1)]
print(*result)
Eolymp #2157. Cəmi

Romanın valideynləri ona n zirvəli və n−1 qəlpəli istiqamətsiz, əlaqəli və çəkili qraf hədiyyə etdilər. Roman bu qrafda bütün zirvə cütlükləri arasındakı yolların ümumi uzunluğunu tapmaq istəyir. Yolun uzunluğu onun daxilində olan qəlpələrin çəkilərinin cəmi kimi müəyyən olunur. Çünki u zirvəsindən v zirvəsinə olan yol ilə v zirvəsindən u zirvəsinə olan yol eyni sayılır, Roman onları bir yol kimi nəzərə alır və fərqləndirmir.

Giriş. İlk sətirdə bir tam ədəd n (2≤n≤105) — qrafdakı zirvələrin sayı verilir.

Növbəti n−1 sətrin hər birində üç tam ədəd verilir: bir qəlpə ilə birləşdirilmiş iki zirvənin nömrələri (zirvələr 1-dən n-ə qədər nömrələnmişdir) və həmin qəlpənin çəkisi.

Çıxış. Bütün fərqli zirvə cütləri arasında olan yolların ümumi uzunluğunu 109-a görə modul ilə verin.

Nümunə giriş 1
3
1 2 1
1 3 3
Nümunə çıxış 1
8
Nümunə giriş 2
6
1 2 5
1 3 1
2 4 2
2 5 4
2 6 3
Nümunə çıxış 2
90
Məsələyə keçid
Həll

Ağacın istənilən zirvəsindən başlayaraq dərinlik üzrə axtarış (DFS) aparın. (u,v) çəkisi w olan bir qəlpəni nəzərdən keçirək. Əgər v zirvəsi kök olan altqrafda c sayda zirvə varsa, o zaman bu qəlpənin bir tərəfində c, digər tərəfində isə n−c zirvə yerləşir. Bu halda (u,v) qəlpəsi c⋅(n−c) sayda müxtəlif zirvə cütü arasında olan yollarda istifadə olunur. Deməli, bu qəlpənin cəmi uzunluğa töhfəsi c⋅(n−c)⋅w olacaq. Sadəcə dərinlik üzrə axtarışla bütün qəlpələri keçərək onların bu şəkildə töhfələrini hesablamaq kifayətdir.

Nümunə

Nümunələrdə verilmiş qraflar aşağıdakı quruluşa malikdir:

İlk nümunədə qəlpələrin ümumi yol uzunluğuna verdiyi töhfəni araşdıraq.

(1,2) qəlpəsi (çəki 1) aşağıdakı 2 yolda iştirak edir:

  • 1−2;

  • 2−1−3;

Bu qəlpənin ümumi cəmi töhfəsi 1⋅2⋅1=2.

(1,3) qəlpəsi (çəki 3) aşağıdakı 2 yolda iştirak edir:

  • 1−3;

  • 2−1−3;

Bu qəlpənin ümumi cəmi töhfəsi 2⋅1⋅3=6.

Yolların ümumi uzunluğu 2+6=8.

İkinci nümunədə (1,2) qəlpəsinin (çəki 5) töhfəsini hesablayın.

2 zirvəsi kök olan altqrafda c=4 zirvə var (özünü də daxil olmaqla). Digər tərəfdə isə n−c=6−4=2 zirvə var. Buna görə, (1,2) qəlpəsi c⋅(n−c)=4⋅2=8 fərqli yolda iştirak edir.

Həqiqətən, bunlar aşağıdakı cütlərdir:

1−2,1−2−4,1−2−5,1−2−6,3−1−2,3−1−2−4,3−1−2−5,3−1−2−6

Bu qəlpənin ümumi uzunluğa verdiyi töhfə:

c⋅(n−c)⋅w=4⋅2⋅5=40
Alqoritmin reallaşdırılması

Modul dəyərini MOD kimi təyin edin.

#define MOD 1000000000

Girişdə verilən çəkili qraf qonşuluq siyahısı g şəklində saxlanılır.

vector<vector<pair<int,int> > > g;

dfs funksiyası v zirvəsindən başlayaraq dərinlik üzrə axtarış aparır və v zirvəsi kök olan altqrafdakı zirvələrin sayını qaytarır (özünü də daxil olmaqla). Bu cəmi cnt dəyişənində saxlayırıq. Əvvəlcə cnt=1 götürülür, çünki altqrafda artıq v zirvəsi var.

int dfs(int v, int p = -1)
{
  int cnt = 1;
  for (auto edge : g[v])
  {
    int to = edge.first;
    int w = edge.second;
C++
7 lines
123 bytes

(v,to) qəlpəsini və onun çəkisi w-nu nəzərdən keçirək. to zirvəsi kök olan altqrafda c sayda zirvə varsa, bu zaman qəlpənin bir tərəfində c, digər tərəfində isə n−c zirvə yerləşir. (v,to) qəlpəsi c⋅(n−c) sayda müxtəlif zirvə cütü arasında olan yollarda iştirak edir. Beləliklə, bu qəlpənin cəmi uzunluğa verdiyi töhfə:

c⋅(n−c)⋅w
    if (to != p)
    {
      int c = dfs(to, v);
      res = (res + 1LL * c * (n - c) * w) % MOD;
      cnt += c;
    }
  }
  return cnt;
}
C++
9 lines
140 bytes

Əsas proqram hissəsi. Giriş çəkili qrafı qonşuluq siyahısına oxuyun.

scanf("%d", &n);
g.resize(n + 1);
for (i = 1; i < n; i++)
{
  scanf("%d %d %d", &u, &v, &d);
  g[u].push_back({v, d});
  g[v].push_back({u, d});
}
C++
8 lines
147 bytes

Dərinlik üzrə axtarışı 1-ci zirvədən başlayın. Qrafın zirvələri 1-dən n-ə qədər nömrələnmişdir.

dfs(1);

Cavabı çap edin.

printf("%lld\n",res);

Java tətbiqi

import java.util.*;
import java.io.*;

class Edge 
{
  int to;
  int weight;
  public Edge(int to, int weight) 
  {
    this.to = to;
    this.weight = weight;
  }
}

public class Main
{
  static int MOD = 1000000000;
  static ArrayList<Edge>[] g;	
  static int n, m;
  static long res;
  
  static int dfs(int v, int p)
  {
    int cnt = 1;
    for(Edge edge : g[v])
    {
      int to = edge.to;
      int w = edge.weight;
      if (to != p)
      {
        int c = dfs(to, v);
        res = (res + 1L * c * (n - c) * w) % MOD;
        cnt += c;
      }
    }
    return cnt;
  }
  
  public static void main(String[] args)
  {
    FastScanner con = new FastScanner(System.in);
    n = con.nextInt();
    g = new ArrayList[n+1]; // yoxlanılmayan tip

    for (int i = 0; i <= n; i++)
      g[i] = new ArrayList<Edge>();

    for (int i = 1; i < n; i++)
    {
      int u = con.nextInt();
      int v = con.nextInt();
      int d = con.nextInt();
      g[u].add(new Edge(v,d));
      g[v].add(new Edge(u,d));
    }
    
    dfs(1,-1);
    System.out.println(res);    
  }
}

class FastScanner 
{
  BufferedReader br;
  StringTokenizer st;

  public FastScanner(InputStream inputStream) 
  {
    br = new BufferedReader(new InputStreamReader(inputStream));
    st = new StringTokenizer("");
  }

  public String next() 
  {
    while (!st.hasMoreTokens()) 
    {
      try 
      {
        st = new StringTokenizer(br.readLine());
      } catch (Exception e) 
      {
        return null;
      }
    }
    return st.nextToken();
  }
 
  public int nextInt() 
  {
    return Integer.parseInt(next());
  }
}
Java
92 lines
1607 bytes

Python tətbiqi

Modul dəyərini MOD kimi təyin edin.

MOD = 1000000000

dfs funksiyası v qovşağından başlayaraq dərinlik üzrə axtarış (DFS) aparır və v qovşağında köklənmiş altqrafda yerləşən qovşaqların sayını qaytarır (özünü də daxil olmaqla). Bu say cnt dəyişəni ilə hesablanır. Başlanğıcda cnt=1 qoyulur, çünki altqraf artıq v qovşağını ehtiva edir.

def dfs(v, p = -1):
  global res
  cnt = 1
  for to, w in g[v]:

(v,to) çəkili kənarını nəzərdən keçirək. Qoy to qovşağında köklənmiş altqrafda c qovşaq olsun. Onda bu kənarın bir tərəfində c, digər tərəfində isə n−c qovşaq yerləşir. (v,to) kənarı c⋅(n−c) fərqli qovşaq cütlüyü arasında olan yolların tərkibində olur. Deməli, bu kənarın bütün yolların ümumi uzunluğuna qatqısı belə hesablanır:

c⋅(n−c)⋅w
    if to != p:
      c = dfs(to, v)
      res = (res + c * (n - c) * w) % MOD
      cnt += c
  return cnt

Proqramın əsas hissəsi. Giriş verilənlərini oxuyun.

n = int(input())
g = [[] for _ in range(n + 1)]
res = 0

for _ in range(n - 1):
  u, v, d = map(int, input().split())
  g[u].append((v, d))
  g[v].append((u, d))
Python
8 lines
162 bytes

Dərinlik üzrə axtarışı 1-ci qovşaqdan başlayın. Qrafın qovşaqları 1-dən n-ə kimi nömrələnib.

dfs(1)

Cavabı çap edin.

print(res)
Eolymp #2923. Tree

N qovşaqdan ibarət köklü bir ağac verilir. Hər bir qovşaq n müxtəlif rəngdən biri ilə rənglənib. Hər bir v qovşağı üçün, həmin qovşaqda köklənmiş altqrafda neçə müxtəlif rəngin olduğunu müəyyənləşdirin.

Giriş. İlk sətirdə bir tam ədəd n (1≤n≤106) verilir. Növbəti n sətir ağacın qovşaqlarını təsvir edir. i-ci sətirdə iki tam ədəd pi​ və ci​ verilir, burada pi​ — i-ci qovşağın valideyni, ci​ isə onun rəngidir (1≤ci​≤n). Ağacın kökü üçün pi​=0.

Çıxış. n tam ədəd çap edin — 1-dən n-ə qədər hər bir qovşaq üçün, onun altqrafında neçə müxtəlif rəngin olduğunu göstərin.

Nümunə giriş
5
2 1
3 2
0 3
3 3
2 1
Nümunə çıxış
1 2 3 1 1
Məsələyə keçid
Həll

Ağacın kökündən başlayaraq dərinlik üzrə keçid (DFS) yerinə yetirin. Hər bir i təpəsi üçün si​ çoxluğu saxlanılır, bu çoxluq həmin təpənin kökü olduğu altqrafdakı bütün təpələrin rənglərini toplayır.

Əgər keçid zamanı j təpəsi i təpəsinin övladıdırsa, sj​ çoxluğu si​ çoxluğuna birləşdirilməlidir.

Beləliklə, i təpəsinin altqrafında olan fərqli rənglərin sayı si​ çoxluğunun ölçüsünə bərabərdir.

Nümunə

Hər təpənin rəngi solda, altqrafında olan rənglərin çoxluğu isə sağda göstərilmişdir.

Algoritmin həyata keçirilməsi

color[i] massivi i təpəsinin rəngini saxlayır. s[i] çoxluğu i təpəsinin altqrafında olan rəngləri toplayır.

İstiqamətli ağac qonşuluq siyahısı kimi g massivində saxlanılır. res[i] isə i təpəsinin altqrafında neçə müxtəlif rəng olduğunu göstərir.

#define MAX 1000010
int color[MAX], res[MAX];
set<int> s[MAX];
vector<vector<int> > g;

dfs funksiyası dərinlik üzrə keçidi v təpəsindən başlayaraq həyata keçirir.

void dfs(int v)
{

İlk olaraq, v təpəsinin öz rəngi s[v] çoxluğuna əlavə olunur.

  s[v].insert(color[v]);

Ağacın (v,to) istiqamətli kənarları üzrə iterasiya aparılır.

  for (int to : g[v])
  {
    dfs(to);

Hər (v,to) kənarı üçün s[to] çoxluğu s[v] çoxluğuna birləşdirilir. Əgər s[v] çoxluğu s[to] çoxluğundan kiçikdirsə, əvvəlcə onlar yer dəyişdirilir. Sonra isə s[to] çoxluğunun bütün elementləri s[v] çoxluğuna əlavə olunur.

    if (s[v].size() < s[to].size()) s[v].swap(s[to]);
    s[v].insert(s[to].begin(), s[to].end());

s[to] çoxluğu təmizlənir — artıq ona ehtiyac yoxdur.

    s[to].clear();
  }

Bundan sonra, res[v] dəyişəninə s[v] çoxluğunun ölçüsü yazılır — bu da v təpəsinin altqrafında olan müxtəlif rənglərin sayıdır.

  res[v] = s[v].size();
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq.

scanf("%d",&n);
g.resize(n+1);
for(i = 1; i <= n; i++)
{
  scanf("%d %d",&p,&c);
  g[p].push_back(i);
  color[i] = c;
}
C++
8 lines
120 bytes

Ağacın kökündən — yəni 0 indeksli təpədən — dərinlik üzrə axtarışa başlayın.

dfs(0);

Cavabı çap edin.

for(i = 1; i <= n; i++)
  printf("%d ",res[i]);
printf("\n");
Eolymp #3983. Mancunian And Colored Tree

Mançester və Liverpul sakinləri uzun və yorucu iş həftəsindən sonra istirahət üçün meşəyə yollanırlar. Orada onlar n təpədən ibarət olan və hər təpəsi c mümkün rəngdən biri ilə boyanmış unikal bir ağacla qarşılaşırlar.

Darixmamaq üçün, onlar məntiqi bacarıqlarını sınamaq qərarına gəlirlər. Ağacın kökü 1 təpəsidir. Hər bir təpə üçün, iştirakçılar eyni rəngə malik olan ən yaxın əcdadı tapmağa çalışırlar.

Giriş. Birinci sətdə iki tam ədəd n və c (1≤n,c≤105) — ağacdakı təpələrin və mümkün rənglərin sayı verilir.

İkinci sət n−1 ədəddən ibarətdir: bunların i-cisi (i+1)-ci təpənin valideynini göstərir.

Üçüncü sət n ədəddən ibarətdir — təpələrin rəngləri. Hər rəng 1 ilə c arasında olan bir tam ədəddir.

Çıxış. Bir sətrə n tam ədəd yazın: hər bir təpə üçün, onunla eyni rəngə malik olan ən yaxın əcdadın nömrəsini çıxışa verin. Əgər belə bir təpə yoxdursa, çıxışa −1 yazın.

Nümunə giriş
5 4
1 1 3 3
1 4 2 1 2
Nümunə çıxış
-1 -1 -1 1 3
Tapşırığı aç
Həllin izahı

Gəlin məsələnin sadələşdirilmiş versiyasını nəzərdən keçirək. Tutaq ki, ağacdakı bütün təpələr eyni rəngdədir. Boş bir stek (stack) yaradıb DFS (dərinlik üzrə axtarış) başladaq. Hər dəfə bir təpəyə daxil olanda onu stekə əlavə edirik. Təpə üzrə işləmə bitdikdə onu stekdən çıxarırıq. DFS tamamlandıqda stek yenidən boş olacaq.

Sual: Vertex v-yə daxil olmamışdan əvvəl, yəni onu stekə atmadan bir an əvvəl, stekin yuxarısında nə olacaq?

İndi əsas məsələyə qayıdaq. c sayda stek yaradın — hər rəng üçün bir stek (məsələn, steklərin massivindən istifadə edərək). Başlanğıcda bütün steklər boşdur. DFS-i kökdən (1 təpəsi) başlayın. Rəngi color olan v təpəsi üçün aşağıdakı addımlar icra olunur:

  • Əgər s[color] steki boş deyilsə, onun yuxarısında olan təpə vertex v ilə eyni rəngə malik ən yaxın əcdaddır. Əgər stek boşdursa, belə bir əcdad yoxdur, və cavab −1 olacaq.

  • v təpəsini s[color] stekinə əlavə edin.

  • Rekursiv olaraq vertex v-nin bütün övladları üçün DFS tətbiq edin.

  • Vertex v üzrə işləmə bitdikdən sonra onu s[color] stekindən çıxarın.

DFS zamanı biz v təpəsində olanda, steklər kökdən v təpəsinə qədər olan unikal yolda yerləşən təpələrin rəngləri haqqında məlumat saxlayır. Xüsusilə, s[color] steki bu yolda rəngi color olan bütün təpələrin nömrələrini saxlayır. Bu təpələr DFS zamanı ziyarət olunma sırasına uyğun şəkildə stekə əlavə olunur.

Nümunə

DFS prosesi 5 təpəsinə çatanda c=4 stekdən ikisi (rəngləri 3 və 4 olanlara uyğun olanlar) boş olacaq.

Rəngi 1 olan stek (1 nömrəli stek) 1 təpəsini, rəngi 2 olan stek (2 nömrəli stek) isə 3 təpəsini saxlayacaq. Çünki 5 təpəsinin rəngi 2-dir, onunla eyni rəngə malik ən yaxın əcdadı 2 nömrəli stekin yuxarısındakı təpə olacaq.

Beləliklə, 5 təpəsinin eyni rəngə malik ən yaxın əcdadı 3-dür.

Alqoritmin reallaşdırılması

Massivləri elan edin.

vector<int> col, res;
vector<vector<int> > g;
vector<stack<int> > s;

dfs funksiyası v təpəsindən başlayaraq dərinlik üzrə axtarış aparır.

void dfs(int v)
{

v təpəsinin rəngi color dəyişənində saxlanılır.

  int color = col[v];

v təpəsi ilə eyni rəngə malik ən yaxın əcdadın nömrəsi res[v] dəyişənində saxlanılır.

  if(s[color].empty())
    res[v] = -1;
  else
    res[v] = s[color].top();

v təpəsini onun rənginə uyğun olan stekə əlavə edin.

  s[color].push(v);

v təpəsinin bütün övladlarını iterasiya edin və hər biri üçün rekursiv olaraq DFS tətbiq edin.

  for (int to : g[v])
    dfs(to);

v təpəsinin emalı başa çatdıqdan sonra (yəni, ondan çıxış zamanı), onu rənginə uyğun olan stekdən çıxarın.

  s[color].pop();
}

Proqramın əsas hissəsi. Giriş verilənlərini oxuyun və ağacı qurun.

scanf("%d %d", &n, &c);
g.resize(n + 1);
for(i = 2; i <= n; i++)
{
  scanf("%d",&val);
  g[val].push_back(i);
}
C++
7 lines
112 bytes

Ağac təpələrinin rənglərini oxuyun.

col.resize(n + 1);
for(i = 1; i <= n; i++)
  scanf("%d",&col[i]);

DFS-i 1 təpəsindən başladın.

s.resize(c + 1);
res.resize(n + 1);
dfs(1);

Cavabı çap edin.

for(i = 1; i <= n; i++)
  printf("%d ",res[i]);
printf("\n");

Python tətbiqi

Rekursiya üçün stek ölçüsünü artırın.

import sys
sys.setrecursionlimit(1000000)

dfs funksiyası v təpəsindən başlayaraq dərinliyinə doğru axtarış (DFS) yerinə yetirir.

def dfs(v):

v təpəsinin rəngi dəyişəndə color dəyişənində saxlanılır.

  color = col[v]

v təpəsi ilə eyni rəngdə olan ən yaxın ata təpənin nömrəsi res[v] dəyişənində saxlanılır.

  if not s[color]:
    res[v] = -1
  else:
    res[v] = s[color][-1]

v təpəsi öz rənginə uyğun olan stekə əlavə olunur.

  s[color].append(v)

v təpəsinin bütün övladları üçün DFS rekursiv şəkildə çağırılır.

  for to in g[v]:
    dfs(to)

v təpəsinin emalı başa çatdıqdan sonra, o, rənginə uyğun olan stekdən çıxarılır.

  s[color].pop()

Proqramın əsas hissəsi. Giriş verilənlərini oxuyun və ağacı qurun.

n, c = map(int, input().split())
lst = list(map(int, input().split()))
g = [[] for _ in range(n + 1)]
for i in range(2, n + 1):
  val = lst[i - 2]
  g[val].append(i)

Ağacın təpələrinin rənglərini oxuyun.

col = [0] * (n + 1)
col[1:] = list(map(int, input().split()))

Dərinliyinə doğru axtarışı 1 təpəsindən başlayın.

s = [[] for _ in range(c + 1)]
res = [0] * (n + 1)
dfs(1)

Cavabı çap edin.

print(*res[1:])
Eolymp #10422. MooTube (Silver)

Boş vaxtlarında Farmer John öz video paylaşım platforması olan MooTube-u yaratdı. MooTube-da onun inəkləri videolar çəkə, paylaşa və kəşf edə bilərlər. İndiyə qədər inəklər n video yükləmişlər, və bu videolar 1-dən n-ə qədər nömrələnmişdir.

Lakin Farmer John inəklərinə yeni videolar tapmaqda necə kömək edəcəyini tam başa düşmür. O, hər video üçün "tövsiyə olunan videolar" siyahısı hazırlamaq istəyir — bu siyahı baxılmış videolara bənzərlik əsasında hazırlanacaq.

İki video arasında nə qədər bənzərlik olduğunu müəyyən etmək üçün John uyğunluq metrikası təqdim edir. O, əllə n−1 video cütünü qiymətləndirir və hər cütə uyğunluq dəyəri təyin edir. Bu cütlərdən istifadə edərək, John videoların hər birini qrafın təpəsi kimi qəbul edib və uyğunluq dəyərləri olan kənarlarla birləşdirir.

Rahatlıq üçün John bu n−1 cütü elə seçir ki, istənilən iki video arasında yalnız bir yol mövcud olsun — yəni, video şəbəkəsi bir ağac təşkil edir.

O, iki video arasındakı uyğunluğu, onları birləşdirən yol boyunca olan kənarların minimum uyğunluğu kimi müəyyənləşdirir.

İndi Farmer John k dəyərini seçmək istəyir ki, MooTube-da olan istənilən video üçün uyğunluğu azı k olan bütün digər videolar tövsiyə kimi göstərilsin. Lakin o qorxur ki, çoxlu tövsiyələr inəklərin süd verməsini dayandıra bilər, buna görə də o, müxtəlif k dəyərləri üçün neçə video tövsiyə ediləcəyini dəqiq hesablamaq istəyir.

Farmer John sizə müraciət edir: sizə bir neçə sorğu verilir, hər biri k dəyəri və bir video nömrəsindən ibarətdir. Hər sorğu üçün, əgər tövsiyə üçün tələb olunan minimum uyğunluq k=ki​ olsa, videoya neçə digər video tövsiyə olunacağını müəyyənləşdirməlisiniz.

Giriş. Birinci sətirdə iki tam ədəd n və q (1≤n,q≤5000) — video sayı və sorğuların sayı verilir.

Növbəti n−1 sətirdə Farmer John tərəfindən qiymətləndirilmiş video cütləri verilir. Hər sətirdə üç tam ədəd pi​,qi​ və ri​ (1≤pi​,qi​≤n,1≤ri​≤109), bu o deməkdir ki, pi​ və qi​ videoları uyğunluq dəyəri ri​ olan kənarla birləşdirilmişdir.

Sonra isə q sorğu verilir. i-ci sorğuda iki tam ədəd ki​ və vi​ (1≤ki​≤109,1≤vi​≤n) verilir — bu o deməkdir ki, Farmer John soruşur: əgər minimum uyğunluq k=ki​ olarsa, vi​ videosu üçün neçə video tövsiyə ediləcək?

Çıxış. q sətir çap edin. i-ci sətirdə Farmer John-un i-ci sorğusunun cavabını verin.

Nümunə. Farmer John videolar arasında aşağıdakı əlaqələri qurmuşdur:

  • Video 1 və video 2 arasında uyğunluq 3-dür,

  • Video 2 və video 3 arasında uyğunluq 2-dir,

  • Video 2 və video 4 arasında uyğunluq 4-dür.

Bu məlumatlara əsasən, digər video cütləri arasındakı uyğunluqları hesablaya bilərik:

  • Video 1 və video 3: uyğunluq = min(3,2)=2,

  • Video 1 və video 4: uyğunluq = min(3,4)=3,

  • Video 3 və video 4: uyğunluq = min(2,4)=2.

İndi isə aşağıdakı sorğular üçün hansı videoların tövsiyə olunacağını nəzərdən keçirək:

  • Video 2 üçün k=1 olduqda, videolar 1, 3 və 4 tövsiyə olunacaq.

  • Video 1 üçün k=3 olduqda, videolar 2 və 4 tövsiyə olunacaq.

  • Video 1 üçün k=4 olduqda, heç bir video tövsiyə olunmayacaq.

Nümunə giriş
4 3
1 2 3
2 3 2
2 4 4
1 2
4 1
3 1
7 lines
34 bytes
Nümunə çıxış
3
0
2
Problemi aç
Həll

Hər bir (ki​,vi​) sorğusu üçün vi​ təpəsindən başlayaraq DFS və ya BFS həyata keçirin. Axtarış zamanı yalnız uyğunluğu ki​ və ya ondan böyük olan kənarları keçin.

Əldə edilə bilən təpələrin sayını res kimi hesablayın, başlanğıc təpə vi​ nəzərə alınmadan. res dəyəri (ki​,vi​) sorğusunun cavabıdır.

Nümunə

Farmer John videolar arasında aşağıdakı əlaqələri qurmuşdur:

  • Video 1 və video 2 arasında uyğunluq 3-dür,

  • Video 2 və video 3 arasında uyğunluq 2-dir,

  • Video 2 və video 4 arasında uyğunluq 4-dür.

Bu məlumatlara əsaslanaraq digər video cütləri arasındakı uyğunluğu hesablaya bilərik:

  • Video 1 və video 3: uyğunluq = min(3,2)=2,

  • Video 1 və video 4: uyğunluq = min(3,4)=3,

  • Video 3 və video 4: uyğunluq = min(2,4)=2.

İndi aşağıdakı sorğular üçün hansı videoların tövsiyə olunacağını baxaq:

  • k=1 üçün video 2-dən: videolar 1, 3 və 4 tövsiyə olunur.

  • k=3 üçün video 1-dən: videolar 2 və 4 tövsiyə olunur.

  • k=4 üçün video 1-dən: heç bir video tövsiyə olunmur.

Alqoritmin realizasiyası

Girişi verilən qrafı qonşuluq siyahısı g şəklində saxlayın.

vector<vector<pair<int, int>>> g;

bfs funksiyası başlanğıc təpədən (start) genişlik üzrə axtarış aparır və başlanğıc təpə istisna olmaqla ziyarət olunan təpələrin sayını qaytarır. Axtarış zamanı yalnız çəkisi ən az k olan kənarlarla hərəkət edilir.

int bfs(int start, int k)
{

Bu məsələdə məsafə massivinə ehtiyac yoxdur — yalnız təpənin ziyarət olunub-olunmadığını izləmək kifayətdir. Əgər i təpəsi ziyarət olunubsa, used[i]=1 təyin olunur.

  vector<int> used(n + 1);
  used[start] = 1;

Bir növbə yaradılır və başlanğıc təpə start ora əlavə olunur.

  queue<int> q;
  q.push(start);

Nəticə dəyişəni res ziyarət olunan təpələrin (başlanğıc təpədən başqa) sayını saxlayır.

  int res = 0;

Genişlik üzrə axtarış başlayır.

  while (!q.empty())
  {

Növbədən təpə v çıxarılır.

    int v = q.front(); q.pop();

Bütün v təpəsinə bitişik kənarlar üzrə iterasiya aparılır.

    for (auto edge : g[v])
    {

(v,to) kənarını və onun çəkisini (kto) götür.

     int to = edge.first;
     int kto = edge.second;

Əgər kənarın çəkisi kto<k olarsa, bu kənar atlanır (bu kənarla hərəkət qadağandır).

      if (kto < k) continue;

Əgər to təpəsi hələ ziyarət olunmayıbsa, onu növbəyə əlavə et, ziyarət olunmuş kimi işarələ və res sayğacını 1 artır.

      if (used[to] == 0)
      {
        q.push(to);
        used[to] = 1;
        res++;
      }
    }
  }
C++
8 lines
108 bytes

Sorğunun cavabını qaytar.

  return res;
}

Əsas proqram hissəsi. Giriş məlumatlarını oxu.

scanf("%d %d", &n, &qu);
g.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
  scanf("%d %d %d", &p, &q, &r);

(p,q) kənarını çəkisi r olmaqla yönsüz şəkildə qonşuluq siyahısına əlavə et.

  g[p].push_back({ q, r });
  g[q].push_back({ p, r });
}

qu sorğularını bir-bir emal et.

while (qu--)
{
  scanf("%d %d", &k, &v);

Təpə v-dən başlayaraq eninə doğru axtarış (BFS) həyata keçirin və bu zaman ziyarət olunan təpələrin sayını çap edin.

  printf("%d\n", bfs(v, k));
}

Alqoritmin reallaşdırılması — dərinlik üzrə axtarış (DFS)

Girişi verilmiş qraf qonşuluq siyahısı g kimi saxlanılır.

vector<vector<pair<int, int>>> g;

dfs funksiyası v təpəsindən başlayaraq dərinlik üzrə axtarış həyata keçirir və ziyarət olunan təpələrin sayını qaytarır. Axtarış zamanı yalnız çəkisi k və ya ondan böyük olan kənarlarla hərəkət etmək icazəlidir.

int dfs(int v, int k)
{

v təpəsini ziyarət olunmuş kimi işarələ.

  used[v] = 1;

v təpəsindən başlayan altqrafda ziyarət olunan təpələrin sayı res dəyişənində saxlanılır.

  int res = 1;

v təpəsinə bitişik bütün kənarları dövr ilə yoxla.

  for (auto edge : g[v])
  {

(v,to) kənarını və onun çəkisini kto götür.

    int to = edge.first;
    int kto = edge.second;

Əgər kto<k olarsa, bu kənar atlanır (bu kənarla hərəkət qadağandır).

    if (kto < k) continue;

Əgər to təpəsi hələ ziyarət olunmayıbsa, to təpəsindən DFS başlat və nəticəni res dəyişəninə əlavə et.

    if (used[to] == 0)
      res += dfs(to, k);
  }
  return res;
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq.

scanf("%d %d", &n, &qu);
g.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
  scanf("%d %d %d", &p, &q, &r);

Çəkisi r olan (p,q) istiqamətsiz kənarını qonşuluq siyahısına əlavə et.

  g[p].push_back({ q, r });
  g[q].push_back({ p, r });
}

qu sorğularını bir-bir emal et.

while (qu--)
{
  scanf("%d %d", &k, &v);

v təpəsindən başlayaraq dərinlik üzrə axtarış həyata keçir. Axtarış zamanı ziyarət olunan təpələrin sayını çap et, başlanğıc təpə v daxil edilmədən.

  used.assign(n + 1, 0);
  printf("%d\n", dfs(v, k) - 1);
}
Eolymp #10653. XOR Sum

n təpəli ağac verilib. Ağacın kənarlarının çəkiləri yalnız 0 və ya 1 ola bilər. Bütün təpə cütlükləri üçün onların XOR cəmini tapmaq tələb olunur. Bütün bu cəmlərin ümumi cəmini hesablayın.

Giriş. Birinci sətrdə n (2≤n≤105) — qrafın təpələrinin sayı verilir. Sonrakı n−1 sətrdə kənarların təsviri verilir. Hər sətrdə 3 tam ədəd verilir: kənarla birləşən təpələrin nömrələri (təpələr 1-dən n-ə kimi nömrələnib) və bu kənarın çəkisi (0 və ya 1).

Çıxış. Bütün təpə cütləri üçün XOR cəmlərinin ümumi cəmini çap edin.

Nümunə giriş
5
1 2 1
2 3 1
2 4 0
4 5 1
Nümunə çıxış
6
Məsələyə keçid
Həll

Dərinlik üzrə axtarış (DFS) istifadə edərək, 1 təpəsi ilə bütün digər təpələr arasındakı XOR cəmlərini hesablayın. 1 və v təpələri arasındakı XOR cəmini x[v] massivində saxlayın. x massivində neçə 1 olduğunu ones, neçə 0 olduğunu isə zeroes kimi təyin edək (ones+zeroes=n). O zaman cavab ones⋅zeroes olacaq.

Nümunə

Nümunədə verilmiş qrafı nəzərdən keçirək.

Hər təpənin yanında 1 ilə həmin təpə arasındakı XOR cəmi x[v] göstərilib. Əgər bəzi v və u təpələri üçün x[v]=x[u] olarsa, o zaman bu iki təpə arasında XOR cəmi 0 olacaq və bu cütlük ümumi cəmə töhfə verməyəcək. Əgər x[v]=x[u] olarsa, bu cüt XOR cəminə 1 töhfə verəcək.

Beləliklə, cavab x[v]=x[u] olan bütün cütlüklərin sayına bərabərdir. Bu say ones⋅zeroes=2⋅3=6 olacaq. XOR cəminə 1 töhfə verən cütlüklər bunlardır: (1,2),(1,4),(2,3),(2,5),(3,4),(4,5).

Alqoritmin həyata keçirilməsi

Girişi qonşuluq siyahısı g şəklində saxlayın. x massivini elan edin.

vector<vector<pair<int,int> > > g;
vector<int> x;

dfs funksiyası dərinlik üzrə axtarış həyata keçirir və 1 və v təpələri arasındakı XOR cəmi x[v] massivində saxlanılır. Hal-hazırkı XOR cəmi cur_xor, v təpəsinin valideyni isə p ilə göstərilir.

void dfs(int v, int cur_xor, int p = -1)
{
  x[v] = cur_xor;
  for (auto z : g[v])
  {
    int to = z.first;
    int w = z.second;
    if (to != p) dfs(to, cur_xor ^ w, v);
  }
}
C++
10 lines
179 bytes

Proqramın əsas hissəsi. Giriş qrafını oxuyun.

scanf("%d", &n);
g.resize(n + 1);
x.resize(n + 1);

for (i = 1; i < n; i++)
{
  scanf("%d %d %d", &u, &v, &d);
  g[u].push_back({v, d});
  g[v].push_back({u, d});
}
C++
10 lines
165 bytes

Dərinlik üzrə axtarışı 1 təpəsindən başlayın.

dfs(1, 0, -1);

x massivində zeroes və ones sayını hesablayın.

ones = 0;
for (i = 1; i <= n; i++)
  if (x[i] == 1) ones++;
zeroes = n - ones;

Cavabı ekrana verin.

printf("%lld\n", 1LL * ones * zeroes);

Python reallaşdırması

dfs funksiyası 1 və v təpələri arasındakı XOR cəmini hesablayan dərinlik üzrə axtarış həyata keçirir. 1 və v arasındakı cari XOR cəmi cur_xor ilə göstərilir. v təpəsinin valideyni p-dir.

def dfs(v, cur_xor = 0, p = -1):
  x[v] = cur_xor
  for to, w in g[v]:
    if to != p:
      dfs(to, cur_xor ^ w, v)

Proqramın əsas hissəsi. Girişi oxuyun.

n = int(input())
g = [[] for _ in range(n + 1)]
for _ in range(n - 1):
  u, v, d = map(int, input().split())
  g[u].append((v, d))
  g[v].append((u, d))

x massivini başlat.

x = [0] * (n + 1)

Dərinlik üzrə axtarışı təpə 1-dən başlayın.

dfs(1)

x massivindəki zeroes və ones sayını hesablayın.

ones = sum(x)
zeroes = n - ones

Cavabı ekrana verin.

print(ones * zeroes)
Eolymp #10654. Unikal Rəng

1-dən n-ə qədər nömrələnmiş n təpədən ibarət bir ağac verilir. Ağacdakı i-ci kənar ai​ və bi​ təpələrini birləşdirir. i-ci təpə ci​ rəngi ilə boyanıb (bu məsələdə rənglər tam ədədlərlə təmsil olunur).

Bir təpə x yaxşı hesab olunur əgər təpə 1-dən təpə x-ə qədər olan ən qısa yol üzərində x-dən başqa elə bir təpə yoxdur ki, x ilə eyni rəngdə olsun.

Bütün yaxşı təpələri tapın.

Giriş. Birinci sətirdə bir tam ədəd n (2≤n≤105) — təpələrin sayı verilir.

İkinci sətirdə n tam ədəd verilir — təpələrin rəngləri: c1​,c2​,…,cn​ (1≤ci​≤105).

Növbəti n−1 sətrin hər birində iki tam ədəd ai​ və bi​ (1≤ai​,bi​≤n) verilir — ağacdakı kənarlar.

Çıxış. Bütün yaxşı təpələrin nömrələrini artan sıra ilə, hər sətrə biri olmaqla çap edin.

Nümunə giriş 1
6
2 7 1 8 2 8
1 2
3 6
3 2
4 3
2 5
7 lines
34 bytes
Nümunə çıxış 1
1
2
3
4
6
Nümunə giriş 2
10
3 1 4 1 5 9 2 6 5 3
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
11 lines
60 bytes
Nümunə çıxış 2
1
2
3
5
6
7
8
7 lines
14 bytes
Məsələyə keçid
Həll

Təpə 1-dən başlayaraq dərinlikə doğru axtarış (DFS) həyata keçirin. Rəngi color[v] olan v təpəsinə daxil olduqda, used[color[v]] dəyişəninin dəyərini 1 vahid artırın. used[color[v]] dəyəri, 1 təpəsindən v təpəsinə qədər (daxil olmaqla) olan yolda bu rəngin neçə dəfə göründüyünü göstərir. Əgər bu rəng yolda yalnız bir dəfə görünürsə, v təpəsi "yaxşı" hesab olunur və nəticə çoxluğuna əlavə olunur.

Təpədən çıxarkən used[color[v]] dəyişəninin dəyərini 1 vahid azaldın.

Nümunə

Birinci nümunədəki qraf aşağıdakı kimidir:

Təpə 5 "yaxşı" deyil, çünki 1−2−5 yolunda həm 1, həm də 5 təpələri eyni rəngdədir.

Təpə 6 isə "yaxşı"dır, çünki 1−2−3−6 yolunda ara təpələrin heç biri 6 ilə eyni rəngə malik deyil.

Alqoritmin implementasiyası

Giriş qrafını qonşuluq siyahısı (g) kimi saxlayın. Aşağıdakı massivləri elan edin:

vector<int> used, color;
vector<vector<int>> g;
set<int> st;

dfs funksiyası dərinliyə doğru axtarışı həyata keçirir. par dəyişəni v təpəsinin valideynidir.

void dfs(int v, int par)
{

Təpə v-nin rəngi color[v]-dir. Bu rəngin yolda göründüyünü qeyd edin:

  used[color[v]]++;

Əgər color[v] rəngi yolda yalnız bir dəfə görünürsə, v təpəsini nəticə çoxluğuna əlavə edin:

  if (used[color[v]] == 1) st.insert(v);

Təpə v-dən çıxan bütün (v,to) kənarları üzrə iterasiya aparın. Əgər to təpəsi v-nin valideyni deyilsə (to=par), ondan dərinliyə doğru axtarışa başlayın. Bu halda to-nun valideyni v olur.

  for (int to : g[v])
    if (to != par) dfs(to, v);

Təpə v-dən çıxarkən, used[color[v]] dəyərini 1 vahid azaldın.

  used[color[v]]--;
}

Proqramın əsas hissəsi. Təpələrin sayını n və rənglər massivini oxuyun.

scanf("%d", &n);
color.resize(n + 1);
for (i = 1; i <= n; i++)
  scanf("%d", &color[i]);

Qrafı oxuyun.

used.resize(100001);
g.resize(n + 1);
for (i = 1; i < n; i++)
{
  scanf("%d %d", &x, &y);
  g[x].push_back(y);
  g[y].push_back(x);
}
C++
8 lines
134 bytes

Təpə 1-dən dərinlik üzrə axtarışa başlayın.

dfs(1, 1);

Yaxşı təpələri çap edin. Onlar st çoxluğunda saxlanılır.

for (int val : st)
  printf("%d\n", val);
Eolymp #10655. Virus Tree 2

1-dən n-ə qədər nömrələnmiş n təpə və n−1 kənardan ibarət bir ağac verilir. i-ci kənar ai​ və bi​ təpələrini birləşdirir.

Sizin ixtiyarınızda k rəng var. Ağacın hər təpəsini bu k rəngdən biri ilə rəngləməlisiniz, aşağıdakı şərti ödəməklə:

  • Əgər iki fərqli təpə x və y arasında olan məsafə 2 və ya daha azdırsa, o zaman x və y fərqli rənglərə malik olmalıdır.

Ağacı bu şərti pozmadan neçə fərqli şəkildə rəngləmək olar? Cavabı 109+7 modulu üzrə hesablayın.

Giriş. Birinci sətirdə iki tam ədəd n və k (1≤n,k≤105) verilir. Növbəti n−1 sətrin hər birində iki tam ədəd ai​ və bi​ (1≤ai​,bi​≤n) — ağacın kənarları verilir.

Çıxış. Ağacı verilmiş qayda ilə neçə müxtəlif şəkildə rəngləmək olar? Cavabı 109+7 modulu üzrə çap edin.

Nümunə giriş 1
4 3
1 2
2 3
3 4
Nümunə çıxış 1
6
Nümunə giriş 2
5 4
1 2
1 3
1 4
4 5
Nümunə çıxış 2
48
Açıq məsələ
Həll

Rəngləməyə təpə 1-dən başlayaq. Bu təpə k fərqli rənglə rənglənə bilər.

Tutaq ki, biz v təpəsindəyik. Əgər onun valideyni yoxdursa (v=1), onun övladları k−1 fərqli rənglə rənglənə bilər. Əgər v-nin valideyni varsa, o zaman övladları k−2 fərqli rənglə rənglənə bilər. Çünki v-nin övladlarının arasındakı məsafə 2-ə bərabərdir və bu səbəbdən onlar eyni rəngdə ola bilməzlər.

Əgər v-nin valideyni varsa, o zaman onun birinci övladı k−2, ikinci övladı k−3 və s. şəkildə rənglənə bilər.

Sadəcə olaraq, hər bir təpə üçün istifadə oluna biləcək rənglərin sayını bir-birinə vurmaq qalır.

Nümunə

Birinci nümunədə ağacı rəngləmək üçün 6 fərqli yol mövcuddur:

İkinci testi nəzərdən keçirək. Hər bir təpənin üzərinə onun neçə fərqli rənglə rənglənə biləcəyini yazaq. Məsələn, 5 təpəsi yalnız 2 rənglə rənglənə bilər, çünki onun rəngi 1 və 4 təpələrinin rəngindən fərqli olmalıdır. Ağacı rəngləməyin ümumi yolları

Alqoritmin reallaşdırılması

Girişi təmsil edən qrafı qonşuluq siyahısı g şəklində yadda saxlayın. Modul üçün sabit MOD təyin edin.

#define MOD 1000000007
vector<vector<int>> g;

dfs funksiyası v təpəsi ilə köklənmiş ağacı MOD=109+7 modulu üzrə neçə yolla rəngləmək mümkün olduğunu qaytarır. v təpəsinin valideyni p, özü isə col rənglə rənglənə bilər.

int dfs(int v, int col, int p = -1)
{
  long long res = col;

Hazırda v təpəsindəyik. cnt dəyişənində v təpəsinin övladları üçün istifadə oluna bilməyəcək rənglərin sayı saxlanılır. Başlanğıcda cnt=1 təyin edilir, çünki övladlar v-nin rəngini ala bilməzlər. Əgər v-nin valideyni varsa (p=−1), o zaman övladlar həmçinin valideynin rəngini də ala bilməz.

  int cnt = 1;
  if (p >= 0) cnt++;

v təpəsinin bütün övladları to üzrə iterasiya aparılır.

  for (int to : g[v])
    if (to != p)
    {

to təpəsi k−cnt fərqli rənglə rənglənə bilər. Hər bir övladdan sonra cnt dəyişəni 1 vahid artır.

      res = (res * dfs(to, k - cnt, v)) % MOD;
      cnt++;
    }
  return res;
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyun.

scanf("%d %d", &n, &k);
g.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  g[b].push_back(a);
}
C++
8 lines
141 bytes

Dərinlik üzrə axtarışı təpə 1-dən başladın. 1 nömrəli təpə k rəngdən biri ilə rənglənə bilər.

res = dfs(1, k);

Cavabı çap edin.

printf("%lld\n", res);

Python reallaşdırılması

Python interpretatorunun yığın dərinliyini müəyyən olunmuş həddə qədər artırın.

import sys
sys.setrecursionlimit(1000000)

Modul üçün sabit MOD təyin edin.

MOD = 1000000007

dfs funksiyası v təpəsi ilə köklənmiş ağacı MOD=109+7 modulu üzrə neçə yolla rəngləmək mümkün olduğunu qaytarır. v təpəsinin valideyni p-dir. v təpəsi col qədər fərqli rənglə rənglənə bilər.

def dfs(v, col, p = -1):
  res = col

Hazırda v təpəsindəyik. cnt dəyişənində v təpəsinin övladları üçün istifadə oluna bilməyəcək rənglərin sayı saxlanılır. Başlanğıcda cnt=1 təyin edilir, çünki övladlar v təpəsinin rəngini ala bilməzlər. Əgər v təpəsinin valideyni varsa (p=−1), o zaman övladlar həmçinin valideynin rəngini də ala bilməzlər.

  cnt = 1
  if p >= 0: cnt += 1

v təpəsinin bütün övladları to üzrə iterasiya aparılır.

  for to in g[v]:
    if to != p:

to təpəsi k−cnt fərqli rənglə rənglənə bilər. Hər bir övladdan sonra cnt dəyişəni 1 vahid artır.

      res = (res * dfs(to, k - cnt, v)) % MOD
      cnt += 1
  return res

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyun.

n, k = map(int, input().split())
g = [[] for _ in range(n + 1)]
for _ in range(n - 1):
  a, b = map(int, input().split())
  g[a].append(b)
  g[b].append(a)

Dərinlik üzrə axtarışı təpə 1-dən başladın. 1 nömrəli təpə k rəngdən biri ilə rənglənə bilər.

res = dfs(1, k)

Cavabı çap edin.

print(res)
Eolymp #10684. Yolların Təkmilləşdirilməsi

Ölkədə n şəhər və n−1 ikitərəfli yol var. Elədir ki, yalnız bu yollardan istifadə etməklə istənilən şəhərdən digərinə getmək mümkündür. Şəhərlər 1-dən n-ə qədər nömrələnmişdir.

Başlanğıcda, bütün yollar pis vəziyyətdə hesab olunur, lakin hökumət bu yollardan bəzilərini yaxşılaşdırmağı planlaşdırır. Vətəndaşlar təkmilləşdirmələrdən yalnız o zaman razı qalacaqlar ki, paytaxtdan (yəni 1-ci şəhərdən) istənilən digər şəhərə gedən yolda ən çox bir pis vəziyyətdə yol olsun.

Tələbi ödəmək üçün yolların bəzilərini yaxşılaşdırmağın neçə mümkün üsulu olduğunu müəyyən edin. Cavab çox böyük ola biləcəyindən onu 109+7 modulu üzrə çap edin.

Giriş. Birinci sətirdə bir tam ədəd n (2≤n≤2⋅105) — ölkədəki şəhərlərin sayı verilir. İkinci sətirdə n−1 ədəd p2​,p3​,p4​,⋯,pn​ (1≤pi​≤i−1) ədədləri verilir. Burada pi​ ədədi pi​ və i şəhərləri arasında bir yolun olduğunu bildirir.

Çıxış. Yolları yaxşılaşdırmağın tələbə uyğun gələn bütün mümkün yollarının sayını 109+7 modulu üzrə çap edin.

Nümunə giriş 1
3
1 1
Nümunə çıxış 1
4
Nümunə giriş 2
6
1 2 2 1 5
Nümunə çıxış 2
15
Problemi aç
Həll

q(v) — v təpəsi ilə köklənmiş altqraf üçün yolları yaxşılaşdırmağın üsullarının sayını göstərsin. to — v təpəsinin övladıdır.

  • Əgər (v,to) yolu pis vəziyyətdə qalırsa, o zaman to təpəsinin altqrafındakı bütün yollar yaxşılaşdırılmalıdır.

  • Əgər (v,to) yolu yaxşılaşdırılırsa, o zaman to təpəsinin altqrafı üçün yolları yaxşılaşdırmağın sayı q(to)-ya bərabərdir.

Əgər to1​,to2​,...,tok​ — v təpəsinin övladlarıdırsa, o zaman

q(v)=(q(to1​)+1)⋅(q(to2​)+1)⋅…⋅(q(tok​)+1)

Əgər v yarpaq təpədirsə (övladı yoxdursa), onda q(v)=1 qəbul edilir.

Nümunə

Məsələdə verilmiş nümunələrə baxaq. Hər təpə üçün f(v) qiymətini qeyd edək.

Birinci nümunədə yolları elə yaxşılaşdırmağın 4 üsulu var ki, şəhər 1-dən istənilən digər şəhərə qədər yolda ən çox bir pis vəziyyətdə yol olsun. Pis vəziyyətdə olan yollar kəsik xətlərlə, yaxşılaşdırılmış yollar isə bərk xətlərlə göstərilib.

Alqoritmin implementasiyası

Bütün hesablamalar MOD=109+7 moduluna görə aparılır.

#define MOD 1000000007

dfs funksiyası f(v) qiymətini hesablayır — v təpəsindən başlayan altqraf üçün yolları yaxşılaşdırmağın üsullarının sayı. v təpəsinin valideyni p-dir.

long long dfs(int v, int p = -1)
{
  long long s = 1;

Əgər to1​,to2​,...,tok​ — v təpəsinin övladlarıdırsa, o zaman

f(v)=(f(to1​)+1)⋅(f(to2​)+1)⋅…⋅(f(tok​)+1)
  for (int to : g[v])
    if (to != p)
    {
      long long sub = dfs(to, v);
      s = (s * (sub + 1)) % MOD;
    }
  return s;
}
C++
8 lines
132 bytes

Giriş məlumatlarını oxuyun və ağacı qurun.

scanf("%d", &n);
g.resize(n + 1);
for (i = 2; i <= n; i++)
{
  scanf("%d", &p);
  g[i].push_back(p);
  g[p].push_back(i);
}
C++
8 lines
124 bytes

Nəticəni hesablayın və çap edin.

res = dfs(1);
printf("%lld\n", res);
Eolymp #11058. Kəpənəyi təqib

Aydın yay günlərində Nyuşa təmiz havada kəpənək tutmağı sevir. Amma bu gün o, xüsusilə hiyləgər bir kəpənəklə qarşılaşdı — kəpənək labirintə uçaraq orada gizlənməyə çalışdı.

Labirint n otaqdan ibarətdir və bu otaqların bəziləri dəhlizlərlə birləşdirilib. Məlumdur ki, istənilən iki otaq arasında yalnız bir sadə yol mövcuddur və bu yol yalnız dəhlizlərdən keçir. Başqa sözlə, labirint n təpə və n−1 qırıqdan ibarət bir ağacdır.

Labirintin girişi 1-ci otaqdadır. Bir otaq yalnız bir başqa otaqla birləşibsə və bu otaq kök deyilsə (yəni 1-ci otaq deyilsə), o zaman bu otaq yarpaq hesab olunur. Hər yarpaq otaqda labirintdən çıxış mövcuddur. Kəpənək öz uçuşuna 1-ci otaqdan başlayır və hansısa çıxışa doğru hərəkət edir. O, sabit sürətlə hərəkət edir, heç vaxt geri dönmür və hər dəqiqədə bir dəhlizdən keçərək qonşu otağa keçir. Bütün dəhlizlər eyni uzunluqdadır.

Kəpənəyi tutmaq üçün Nyuşa dostlarının köməyinə müraciət etmək qərarına gəldi. Başlanğıcda hər biri istənilən çıxışı olan otaqda yerləşə bilər. Kəpənək çıxışlardan birinə doğru hərəkət etməyə başladığı anda dostlar da dərhal hərəkətə keçə bilərlər. Onlar da kəpənəklə eyni sürətlə hərəkət edirlər. Əgər onlardan biri kəpənəklə eyni otaqda və ya bir dəhlizin ortasında qarşılaşarsa, kəpənək tutulmuş sayılır. Əgər kəpənək heç bir dostla qarşılaşmadan çıxışa çatsa, azad olur.

Nyuşaya kömək edin — istənilən çıxış seçimi üçün kəpənəyin tutulacağına zəmanət verən minimum dost sayını müəyyən edin.

Giriş. İlk sətirdə bir tam ədəd n (2≤n≤200000) — labirintdəki otaqların sayı verilir.

Növbəti n−1 sətirin hər birində iki tam ədəd u və v (1≤u,v≤n,u=v) verilir — dəhlizlə birləşdirilmiş otaqların nömrələri.

Zəmanət verilir ki, verilmiş dəhliz sistemi bir ağac təşkil edir.

Çıxış. Tək bir tam ədəd çap edin — kəpənəyin tutulmasına zəmanət vermək üçün lazım olan minimum dostların sayı.

Nümunə giriş 1
3
1 2
1 3
Nümunə çıxış 1
2
Nümunə giriş 2
4
1 2
2 3
2 4
Nümunə çıxış 2
1
Problemi aç
Həll

Birinci təpədən başlayaraq dərinlik üzrə ilk keçid (DFS) yerinə yetirin. Hər bir təpə üçün iki qiymət hesablayın:

  • d[v] — təpə 1-dən təpə v-yə olan məsafə. Əgər p təpə v-nin valideynidirsə, onda

    d[v]=d[p]+1
  • h[v] — təpə v-dən öz altqrafında yerləşən ən yaxın yarpaq təpəyə olan məsafə. Əgər to1​,to2​,...,tok​ — v təpəsinin övladlarıdırsa, onda

    h[v]=1+min(h[to1​],h[to2​],...,h[tok​])
  • Əgər v ağacın yarpaq təpəsidirsə, o zaman h[v]=0 təyin edin.

Sonra ikinci DFS keçidi edin. Bu keçid zamanı res[v] qiymətini hesablayın — əgər kəpənək bu altqrafa daxil olarsa, onu tutmaq üçün v təpəsinin altqrafındakı yarpaqlarda yerləşdirilməli olan dostların minimal sayı.

Əgər h[v]≤d[v], onda res[v]=1. Bu halda v altqrafında ən yaxın dərinlikdə yerləşən yarpaq təpəyə bir dost yerləşdirmək kifayətdir: bu dost kəpənək kökdən gələnə qədər v təpəsinə çatacaq.

Əks halda, əgər to1​,to2​,...,tok​ — v təpəsinin övladlarıdırsa, onda

res[v]=res[to1​]+res[to2​]+...+res[tok​]

Əgər kəpənək v təpəsində tutulmayıbsa, onu v övladlarının altqraflarında tutmaq üçün hazır olmalıyıq.

Problemin son cavabı res[1] olacaq.

Nümunə

Birinci nümunədə (soldakı diaqramda göstərilmişdir) iki dost tələb olunur və onlar çıxışlarda yerləşdirilməlidirlər. Kəpənək 1-ci təpədən ya 2-yə, ya da 3-ə uça bilər. Hər iki halda dostlardan biri onu qarşılayacaq.

İkinci nümunədə (sağdakı diaqramda) bir dost kifayətdir, hansı ki ya 3, ya da 4 təpəsində yerləşə bilər. Kəpənək bir dəqiqədə 1-dən 2-yə uçar, həmin vaxtda dost da 2-yə çataraq onu tutur.

Növbəti nümunəyə baxaq.

Bu halda Nyuşaya kəpənəyi tutmaq üçün üç dost lazım olacaq.

Alqoritmin implementasiyası

Sonsuzluq sabitini və massivləri təyin edin.

#define INF 2000000000
vector<vector<int>> g;
vector<int> d, h, res;

dfs (v,p,cnt) funksiyası təpə v-dən başlayaraq dərinlik üzrə ilk keçidi (DFS) yerinə yetirir və h[v] qiymətini qaytarır. Burada p təpə v-nin valideyni, cnt isə təpə 1-dən v-yə olan məsafəni göstərir. Keçid zamanı hər bir v təpəsi üçün d[v] və h[v] qiymətləri hesablanır.

int dfs(int v, int p = -1, int cnt = 0)
{

cnt dəyişəni, yəni 1-dən v-yə olan məsafə, d[v]-də saxlanılır.

  d[v] = cnt;
  int height = INF;

Təpə v-nin bütün övladlarını keçin. v→to qırağını nəzərdən keçirin. Əgər to=p-dirsə, bu halda keçidi atlayın.

  for (int to : g[v])
    if (to != p) 

height dəyişənində aşağıdakı qiymət hesablanır:

min(h[to1​],h[to2​],...,h[tok​]),

burada to1​,to2​,...,tok​ təpə v-nin övladlarıdır.

height=min(height,dfs(to,v,cnt+1));

Əgər height=INF-dirsə, onda v təpəsi yarpaqdır və h[v]=0 təyin edilir. Əks halda, qaytarılan qiymət:

h[v]=1+min(h[to1​],h[to2​],...,h[tok​])
  return h[v] = (height == INF) ? 0 : 1 + height;
}

dfs2 (v,p) funksiyası təpə v-dən başlayaraq DFS yerinə yetirir və res[v] qiymətini qaytarır. Burada p təpə v-nin valideynidir.

int dfs2(int v, int p = -1)
{
  int s = 0;

t1​,t2​,...,tk​ — v təpəsinin övladlarıdır. s dəyişənində aşağıdakı cəmi hesablayın:

res[to1​]+res[to2​]+...+res[tok​]
  for (int to : g[v])
    if (to != p)
    {
      dfs2(to, v);
      s += res[to];
    }

Əgər h[v]≤d[v]-dirsə, bir dost kifayətdir və res[v]=1 olacaq. Əks halda

res[v]=res[to1​]+res[to2​]+...+res[tok​]=s
  return res[v] = (h[v] <= d[v]) ? 1 : s;
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyun. Qrafı qurun.

scanf("%d", &n);
g.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  g[b].push_back(a);
}
C++
8 lines
134 bytes

Massivləri (arayları) başladın.

d.resize(n + 1);
h.resize(n + 1);
res.resize(n + 1);

İki dəfə dərinlik üzrə keçid (DFS) həyata keçirin. Birinci keçid hər bir v təpəsi üçün d[v] və h[v] qiymətlərini hesablayır.

dfs(1);
dfs2(1);

Cavabı çap edin — kəpənəyi tutmaq üçün lazım olan minimum dostların sayı.

printf("%lld\n", res[1]);
Eolymp #11153. Kefa və Park

Kefa ilk böyük maaşını qeyd etmək üçün restorana getmək qərarına gəldi.

O, qeyri-adi bir parkın yaxınlığında yaşayır. Park n təpədən ibarət köklü bir ağacdır və kök 1-ci təpədir. Kefa-nın evi də 1-ci təpədə yerləşir. Təəssüf ki, park pişiklərlə doludur və Kefa artıq hansı təpələrdə pişiklərin olduğunu müəyyən edib.

Parkın yarpaq təpələrində restoranlar yerləşir. Kefa restoran seçmək istəyir, lakin o, pişiklərdən çox qorxur. Buna görə də, əgər evindən restorana qədər yolda m-dən çox ardıcıl pişik olan təpə varsa, o, həmin restorana getməyəcək.

Sizin vəzifəniz — Kefa-nın təhlükəsiz şəkildə gedə biləcəyi restoranların sayını hesablamaqdır.

Giriş. Birinci sətrdə iki tam ədəd verilir: n və m (2≤n≤105,1≤m≤n) — ağacdakı təpələrin sayı və Kefa-nın dözə biləcəyi maksimum ardıcıl pişik olan təpə sayı.

İkinci sətrdə n tam ədəd verilir: a1​,a2​,...,an​, burada hər bir ai​ ya 0 (təpədə pişik yoxdur), ya da 1 (təpədə pişik var) olur.

Sonrakı n−1 sətirdə ağacın qıraqları xi​ yi​ (1≤xi​,yi​≤n,xi​=yi​) formatında verilir, burada xi​ və yi​ bir qıraqla birləşdirilmiş təpələrdir.

Verilən qıraq dəstinin ağac yaratdığına zəmanət verilir.

Çıxış. Kefa-nın evindən başlayaraq yolu boyunca m-dən artıq ardıcıl pişik olmayan yarpaq təpələrə çatmaq imkanlarının sayını çap edin.

Nümunə giriş 1
7 1
1 1 0 0 0 0 1
1 2
1 3
2 4
2 5
3 6
3 7
8 lines
42 bytes
Nümunə çıxış 1
2
Nümunə giriş 2
8 2
1 1 0 1 0 1 0 1
1 2
2 3
2 5
2 6
3 4
6 7
6 8
9 lines
48 bytes
Nümunə çıxış 2
2
Məsələyə keçid
Həll

Kefa-nın evinin yerləşdiyi 1-ci təpədən dərinlik üzrə axtarış (DFS) başladın. dfs funksiyasının parametrindən biri ardıcıl pişik olan təpələrin sayını saxlayacaq. Əgər təpə v-də bu say m-dən çoxdursa, həmin təpədən DFS davam etdirilmir. Yarpaq təpələrin sayı sayılır. Təpə yarpaqdırsa, onun dərəcəsi 1-dir və o, kök təpə (yəni 1) deyil.

Nümunə

Birinci və ikinci nümunədəki ağaclara baxaq.

Birinci nümunədə Kefa yalnız bir ardıcıl pişik olan təpədən keçə bilər. O, 6 və 7-ci təpələrdəki restoranlara çata bilər.

İkinci nümunədə Kefa iki ardıcıl pişik olan təpədən keçə bilər. O, 4 və 5-ci təpələrdəki restoranlara çata bilər.

Alqoritmin reallaşdırılması

Pişiklərin yerləşdiyi məlumatlar cats massivində saxlanılır. Ağac isə bitişik siyahı şəklində g massivində saxlanılır.

vector<int> cats;
vector<vector<int> > g;

dfs funksiyası v təpəsindən dərinlik üzrə axtarış aparır və bu təpədən hansı restoranlara çatmaq mümkün olduğunu qaytarır (şərtlə ki, yol boyunca ardıcıl pişik olan təpələrin sayı m-dən çox olmasın). prev — v təpəsinin valideynidir. cnt isə v daxil olmaqla indiyə qədər ardıcıl pişik olan təpələrin sayıdır.

int dfs(int v, int prev, int cnt)
{

Əgər artıq m-dən çox ardıcıl pişik olan təpələr keçilibsə, axtarış davam etmir.

  if (cnt > m) return 0;

Əgər yarpaq təpədəyiksə, çatmaq mümkün olan restoranların sayını artır.

  if (g[v].size() == 1 && prev != -1) return 1;

res dəyişənində v təpəsindən çatmaq mümkün olan restoranların sayı toplanır.

  int res = 0;

v təpəsindən çıxan bütün (v,to) qıraqlarını gəz. Əgər to v-nin valideynidirsə, bu təpəyə keçmə.

  for (int to : g[v])
    if (to != prev) 
    {

Əgər to təpəsində pişik yoxdursa, c=0 təyin et. Əks halda c=cnt+1. c dəyişəni to təpəsinə qədər (daxil olmaqla) olan ardıcıl pişikli təpələrin sayını saxlayır.

      int c = (cats[to] == 0) ? 0 : cnt + 1;

to təpəsindən DFS-i davam etdir.

      res += dfs(to, v, c);
    }

Nəticəni qaytar.

  return res;
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxu.

scanf("%d %d", &n, &m);
cats.resize(n + 1);
for (i = 1; i <= n; i++)
  scanf("%d", &cats[i]);

Ağacı qur.

g.resize(n + 1);
for (i = 1; i < n; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  g[b].push_back(a);
}
C++
7 lines
113 bytes

Nəticəni çap et. dfs funksiyası 1 təpəsindən çağırılır və məsələnin cavabını qaytarır. Çünki 1 təpəsinin valideyni yoxdur, ikinci parametr kimi −1 ötürülür. 1 təpəsindən başladıqda, artıq cats[1] qədər pişiklə qarşılaşmışıq.

printf("%d\n", dfs(1, -1, cats[1]));

Python reallaşdırılması

dfs funksiyası v təpəsindən dərinlik üzrə axtarış aparır və bu təpədən hansı restoranlara çatmaq mümkün olduğunu qaytarır (şərtlə ki, yol boyunca ardıcıl pişik olan təpələrin sayı m-dən çox olmasın). prev — v təpəsinin valideynidir. cnt isə v daxil olmaqla indiyə qədər ardıcıl pişik olan təpələrin sayıdır.

def dfs(v, prev, cnt):

Əgər artıq m-dən çox ardıcıl pişik olan təpə keçilibsə, axtarış davam etmir.

  if cnt > m:
    return 0

Əgər yarpaq təpədəyiksə, çatmaq mümkün olan restoranların sayını artır.

  if len(g[v]) == 1 and prev != -1:
    return 1

res dəyişənində v təpəsindən çatmaq mümkün olan restoranların sayı toplanır.

  res = 0

v təpəsindən çıxan bütün (v,to) qıraqlarını gəz. Əgər to v-nin valideynidirsə, bu təpəyə keçmə.

  for to in g[v]:
    if to != prev:

Əgər to təpəsində pişik yoxdursa, c=0 təyin et. Əks halda c=cnt+1. c dəyişəni to təpəsinə qədər (daxil olmaqla) olan ardıcıl pişikli təpələrin sayını saxlayır.

      c = 0 if cats[to] == 0 else cnt + 1

to təpəsindən DFS-i davam etdir.

      res += dfs(to, v, c)

Nəticəni qaytar.

  return res

Əsas hissə. Giriş məlumatlarını oxu.

n, m = map(int, input().split())
cats = [0] + list(map(int, input().split()))

Ağacı qur.

g = [[] for _ in range(n + 1)]
for _ in range(n - 1):
  a, b = map(int, input().split())
  g[a].append(b)
  g[b].append(a)

Nəticəni çap et. dfs funksiyası 1 təpəsindən çağırılır və məsələnin cavabını qaytarır. Çünki 1 təpəsinin valideyni yoxdur, ikinci parametr kimi −1 ötürülür. 1 təpəsindən başladıqda, artıq cats[1] qədər pişiklə qarşılaşmışıq.

print(dfs(1, -1, cats[1]))
Eolymp #11609. Cütlükləri tapmaq

1-dən n-ə qədər nömrələnmiş n təpəli bir ağac verilir. 1 təpəsi kök kimi qəbul olunur. Hər iki təpə arasında yalnız bir sadə yol mövcuddur.

d(i,j) — i təpəsindən j təpəsinə qədər olan yoldakı qıraq (kənar) sayıdır.

Elə (i,j) cütlüklərinin sayını tapın ki, aşağıdakı bərabərlik ödənmiş olsun:

d(i,j)=d(i,1)−d(j,1)

Giriş. Birinci sətirdə bir tam ədəd n (1≤n≤105) — ağacdakı təpələrin sayı verilir.

Sonrakı n−1 sətrin hər birində ağacdakı qıraqlar təsvir olunur: hər biri iki tam ədəddən ibarət olan u,v cütü.

Çıxış. d(i,j)=d(i,1)−d(j,1) bərabərliyini ödəyən (i,j) cütlüklərinin sayını çap et.

Nümunə giriş
5
1 2
2 3
2 4
4 5
Nümunə çıxış
13
Məsələyə keçid
Həll

d(i,j)=d(i,1)−d(j,1) şərti yalnız və yalnız j təpəsi i təpəsinin DFS-də (dərinlik üzrə axtarış) əcdadı olduqda doğrudur.

Gəlin bir nümunəyə baxaq:

  • d(4,1)=2, d(4,1)−d(1,1)=2−0=2;

  • d(6,3)=2, d(6,1)−d(3,1)=3−1=2;

  • d(2,2)=0, d(2,1)−d(2,1)=1−1=0;

  • d(6,1)=3, d(6,1)−d(1,1)=3−0=3;

Gəlin h[v] ilə təpə v-nin dərinliyini təyin edək — bu, kökdən (1 təpəsindən) v-yə qədər olan yol üzərindəki təpələrin sayıdır. Şəkildə hər bir təpənin yanında onun dərinliyi göstərilmişdir.

İndi i təpəsini sabit saxlayaraq soruşaq: neçə j təpəsi var ki, d(i,j)=d(i,1)−d(j,1) bərabərliyi ödənir?

Məsələn, i=6 üçün belə 4 təpə var: j=1,3,4,6. Burada h[6]=4. Deməli, sabit bir i üçün h[i] sayda uyğun j mövcuddur.

Buna görə də məsələni həll etmək üçün hər bir təpə üçün h[v] dərinliyini hesablamaq və sonra bütün bu dərinliklərin cəmini tapmaq lazımdır.

Nümunə

Gəlin nümunədəki qrafı nəzərdən keçirək. Hər bir təpənin yanında onun dərinliyi göstərilmişdir.

Bütün təpələrin dərinliklərinin cəmi: 1+2+3+3+4=13.

Alqoritmin implementasiyası

Qrafın qonşuluq siyahısını g kimi müəyyən edək. Təpələrin dərinlikləri h massivində saxlanılır.

vector<vector<int> > g;
vector<int> h;

dfs funksiyası hər bir təpənin dərinliyini hesablayır. v təpəsinin valideyni p təpəsidir.

int dfs(int v, int p)
{
  for (int to : g[v])

(v,to) qırağını nəzərdən keçir. Əgər to təpəsi v-nin valideyni deyilsə, onun dərinliyini hesabla və ondan DFS başlat.

    if (to != p)
    {
      h[to] = h[v] + 1;
      dfs(to, v);
    }
  return h[v];
}
C++
7 lines
88 bytes

Proqramın əsas hissəsi. Ağacdakı təpələrin sayını oxu.

scanf("%d", &n);
g.resize(n + 1);

Ağacı qur.

for (i = 2; i <= n; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  g[b].push_back(a);
}

1 təpəsinin dərinliyini 1 olaraq təyin et.

h.resize(n + 1);
h[1] = 1;

DFS-i 1 təpəsindən başlat.

dfs(1, -1);

Cavabı hesabla — bütün təpələrin dərinliklərinin cəmi.

res = 0;
for (i = 1; i <= n; i++)
  res += h[i];

Cavabı çap et.

printf("%lld\n", res);
Eolymp #11724. Yoluxmuş ağac

İkiyollu bir ağac verilib, yəni n təpə və n−1 qıraqlı, dövrə olmayan və əlaqəli qraf. Hər bir təpənin dərəcəsi maksimum 3-dür. Kök təpə 1-dir və onun dərəcəsi 2-dən artıq deyil.

Təəssüf ki, kök təpəsi yoluxub. Aşağıdakı proses n dəfə təkrarlanır:

  • Hüseyn hər dövrədə ya hələ yoluxmamış (və silinməmiş) bir təpəni seçib onu və ona birləşən bütün qıraqları silir, ya da heç bir şey etmir.

  • Bundan sonra yoluxma prosesi baş verir: artıq yoluxmuş təpələrə birbaşa bağlı olan bütün təpələr yoluxur (əvvəlki yoluxmuşlar yoluxmuş qalır).

Hüseynə maksimum neçə təpəni yoluxmadan xilas edə biləcəyini tapmağa kömək et (qeyd edək ki, silinmiş təpələr xilas olunmuş sayılmır).

Giriş. İlk sətirdə təpələrin sayı n (2≤n≤3⋅105) verilir.

Növbəti n−1 sətirdə ui​ və vi​ (1≤ui​,vi​≤n) cütlükləri verilir — bu, ağacdakı bir qırağı təsvir edir.

Zəmanət verilir ki, verilmiş qraf kökü 1 olan iki yollu ağacdır.

Çıxış. Xilas edilə biləcək maksimum təpələrin sayını verin.

Nümunə giriş 1
4
1 2
2 3
2 4
Nümunə çıxış 1
2
Nümunə giriş 2
7
1 2
1 5
2 3
2 4
5 6
5 7
7 lines
26 bytes
Nümunə çıxış 2
2
Problemi aç
Həll

Hər bir v təpəsi üçün onun kök olduğu altqrafdakı təpələrin sayını sum[v] kimi hesablayırıq. Əgər to1​,to2​,...,tok​ — v təpəsinin övladlarıdırsa, o zaman

sum[v]=1+sum[to1​]+sum[to2​]+...+sum[tok​]

Əgər v təpəsi yarpaqdırsa, onda sum[v]=1.

dp[v] — kökü v olan altqrafda yoluxmadan xilas edilə biləcək təpələrin sayıdır (əgər yalnız v təpəsi yoluxubsa).

Əgər v təpəsinin yalnız bir övladı to varsa, onda dp[v]=sum[to]−1 (çünki to silinir və xilas olunmuş sayılmır).

Əgər v təpəsinin iki övladı var — to1​ və to2​, o zaman iki variant var:

  • to1​ təpəsini silmək və to2​ altqrafındakı təpələri xilas etmək: sum[to1​]−1+dp[to2​].

  • to2​ təpəsini silmək və to1​ altqrafındakı təpələri xilas etmək: sum[to2​]−1+dp[to1​].

Bu iki variantdan maksimumunu seçirik.

DFS zamanı (v,to) qırağını keçərkən aşağıdakı şəkildəki vəziyyəti nəzərdən keçirək:

tov təpəsinin ikinci övladı a-dır. Biz dp[to]+sum[a]−1 ifadəsini hesablamaq istəyirik. Bunun üçün yalnız v və to təpələrindən istifadə edirik. Aşağıdakı bərabərlik doğrudur:

sum[v]=1+sum[to]+sum[a]

Bundan belə nəticə çıxır ki:

sum[a]=sum[v]−sum[to]−1

Bu halda,

dp[to]+sum[a]−1=dp[to]+sum[v]−sum[to]−2

Beləliklə, təpə v üçün övladları to üzərində iterasiya edib aşağıdakı maksimumu tapırıq:

dp[v]=max(dp[to]+sum[v]−sum[to]−2)

Nümunə

Birinci nümunədə Huseyn 2-ci təpəni ilk gediş olaraq silməklə 3 və 4 təpələrini xilas edə bilər.

İkinci nümunəyə baxaq. Hər təpənin yanında sum[v]/dp[v] dəyərləri qeyd olunmuşdur. Huseynin seçdiyi təpələr yaşıl rənglə göstərilmişdir.

Məsələn,

dp[1]=max(sum[2]−1+dp[5],sum[5]−1+dp[2])=max(2,2)=2,sum[1]=1+sum[2]+sum[5]=1+3+3=7
Alqoritmin implementasiyası

Massivləri elan edək:

#define MAX 300005
int sum[MAX], dp[MAX];
vector<vector<int>> g;

dfs funksiyası təpə v-dən başlayaraq DFS icra edir. p — v təpəsinin valideynidir.

void dfs(int v, int p = -1)
{

sum[v] və dp[v]-ni ilkinləşdiririk:

  sum[v] = 1;
  dp[v] = 0;

Təpə v-nin övladları üzrə DFS icra edirik:

  for (int to : g[v])
    if (to != p)
    {
      dfs(to, v);
      sum[v] += sum[to];
    }

dp[v] dəyərini hesablayaq:

  for (int to : g[v])
    if (to != p)
    {
      dp[v] = max(dp[v], sum[to] - 1); // əgər yalnız bir övladı varsa
      dp[v] = max(dp[v], dp[to] + sum[v] - sum[to] - 2); // əks halda, maksimumu götür
    }
}
C++
7 lines
211 bytes

Proqramın əsas hissəsi. Giriş məlumatlarını oxu.

scanf("%d", &n);
g.clear(); g.resize(n + 1);
memset(dp, 0, sizeof(dp));
memset(sum, 0, sizeof(sum));
for (i = 1; i < n; i++)
{
  scanf("%d %d", &u, &v);
  g[u].push_back(v);
  g[v].push_back(u);
}
C++
10 lines
197 bytes

Dərinlikə-öncə axtarışı 1-ci təpədən başlat.

dfs(1);

Cavabı çap et.

printf("%d\n", dp[1]);

Məsələlərin siyahısı

  • 977. Bu, ağacdirmi?

  • 978. Ağac qur

  • 11263. Cüt zirvələr ağacı

  • 11429. Tabeçilikdə olanlar

  • 11428. Ağacın diametri

  • 11430. Ağac məsafələri I

  • 2157. Cəmi

  • 2923. Ağac

  • 3983. Mancunian və rəngli ağac

  • 10422. MooTube (Gümüş)

  • 10653. XOR Cəmi

  • 10654. Unikal rəng

  • 10655. Virus ağacı 2

  • 10684. Yolların yaxşılaşdırılması

  • 11058. Kəpənəyin dalınca

  • 11153. Kefa və park

  • 11609. Cütlüklərin tapılması

  • 11724. Yoluxmuş ağac


15

Şərhlər (5)

Yüklənir

Bir an, serverdən məlumat alınır
user767927•2 ay öncə•2-ci təftiş

сайт розповідає з чого складається, як зробити

0
user897042•4 ay öncə

what a community i asked for help and noone cares.

0
user897042•4 ay öncə

https://codeforces.com/contest/315/problem/B how to solve this problem i wrote two updates one for assinging and second for interval with lazy. but it doesnt seem to work because two seperate updates doesnt have link between them.

0
medv•4 ay öncə

user897042 https://site.ada.edu.az/~medv/acm/Docs%20e-olimp/Volume%209/853_English.htm

1
user897042•4 ay öncə

user897042 need help on these two too:https://codeforces.com/contest/1857/problem/Ghttps://codeforces.com/contest/1702/problem/G

0