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 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.
zirvəli ağacda həmişə dəqiq olaraq 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:
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ə (, 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.
zirvəsi və kənarı olan əlaqəli qraf.
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) {} };
Ümumi ağac:
struct Node { int val; vector<Node*> children; Node(int v) : val(v) {} };
zirvəli bir qrafın ağac olması üçün və yalnız o halda:
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 -ə bərabərdirsə, deməli qraf bağlıdır.
. Əgər qraf ağacdırsa, o zaman kənara malikdir.
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.
və şərtləri və ya və şə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.
Qrafın qonşuluq matrisası və ziyarət statusunu saxlayan massivini elan edək.
#define MAX 110 int g[MAX][MAX], used[MAX];
dfs funksiyası zirvə -dən başlayaraq dərinlikə görə axtarış aparır. zirvəsinin valideyni -dir. Əgər sikl aşkar edilərsə, dəyişəni edilir.
void dfs(int v, int p) {
Əgər qraf artıq ağac deyilsə , axtarışı davam etdirməyə ehtiyac yoxdur.
if (flag) return;
Zirvə ziyarət olunmuş kimi işarələnir.
used[v] = 1;
Zirvə ziyarət olunubsa, dəyişəni vahid artırılır.
c++;
Kənar geri kənar (back edge) olacaq və sikl yaradacaq, əgər və zirvəsi artıq ziyarət olunubsa . Əgər sikl aşkar edilərsə, təyin olunur. Əgər sikl yoxdursa, axtarış 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ı dəyişənində saxlanılır. Əgər qrafda sikl yoxdursa, olur. Əgər sikl aşkar edilərsə, təyin olunur.
c = 0; flag = 0;
Bütün zirvələr başlanğıcda ziyarət olunmamış kimi işarələnir ( massivini sıfırla).
memset(used,0,sizeof(used));
Dərinlikə görə axtarışı -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 verilir.
dfs(0,-1);
Əgər (sikl var) və ya (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ə -dən başlayaraq dərinlikə görə axtarış aparır. zirvəsinin valideyni -dir. Əgər sikl aşkar edilərsə, dəyişəni 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ə , axtarışı davam etdirməyə ehtiyac yoxdur.
if flag: return
Zirvə ziyarət olunmuş kimi işarələnir.
used[v] = 1
Zirvə ziyarət olunub, dəyişəni artırılır.
c += 1
Əgər və zirvəsi artıq ziyarət olunubsa , onda kənarı geri kənardır və sikl yaradır. Əgər sikl aşkar edilərsə, qoyulur. Əks halda, axtarış 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. dəyərini oxu.
n = int(input())
Dərinlikə görə axtarış zamanı ziyarət olunan zirvələrin sayı dəyişənində saxlanılır. Əgər qrafda sikl yoxdursa, . Sikl aşkar edilərsə, 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 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ışı 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 ötürülür.
dfs(0, -1)
Əgər geri kənar varsa və ya qraf əlaqəli deyilsə , o zaman qraf ağac deyil.
if flag or c != n: print("NO") else: print("YES")
Qrafın qonşuluq matrisini və istifadə olunmuş zirvələri saxlayan massivini elan et.
#define MAX 101 int g[MAX][MAX], used[MAX];
dfs funksiyası 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ə ziyarət olundu, dəyişənini artır.
c++;
zirvəsindən keçid mümkün olan bütün zirvələrini yoxla. keçidi mümkündür əgər:
kənarı mövcuddur, yəni ;
zirvəsi hələ ziyarət olunmayıb
Əgər hər iki şərt ödənilirsə, dərinlikə görə axtarışı 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. dəyərini oxu.
scanf("%d", &n);
Qrafdakı kənarların sayını 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 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ə və kənarları eyni sayılır. Ona görə dəyişənini -yə böl.
Edges /= 2;
Dərinlikə görə axtarış zamanı ziyarət olunan zirvələrin sayını dəyişənində saxla.
c = 0;
Dərinlikə görə axtarışı -ci zirvədən başlat.
dfs(1);
Qraf ağacdır, əgər kənarların sayı -ə bərabərdirsə və o, bağlıdırsa .
if ((Edges == n - 1) && (c == n)) printf("YES\n"); else printf("NO\n");
Python implementasiyası — 1) və 2) şərtlərinin yoxlanması
dfs funksiyası zirvəsindən dərinliyə görə axtarışı həyata keçirir.
def dfs(v): global c
Zirvə ziyarət olunduğu kimi işarələnir.
used[v] = 1
Zirvə ziyarət olunubsa, dəyişəni artırılır.
c += 1
Zirvə -dən çıxan keçidlər üzrə zirvələrinə baxılır. keçidi mümkündür, əgər:
kənarı mövcuddursa, yəni ;
zirvəsi hələ ziyarət olunmayıbsa .
Əgər hər iki şərt ödənirsə, axtarış 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. dəyərini oxu.
n = int(input())
Qrafdakı kənarların sayını dəyişənində saxla.
Edges = 0
Bütün zirvələr ilkin olaraq ziyarət olunmamış sayılır (massiv sıfırlarla başlanır).
used = [0] * (n + 1)
Sıfırlarla doldurulmuş başlanğıc vəziyyətində qonşuluq matrisası yaradılır.
g = [[0] * (n + 1) for _ in range(n + 1)]
Qonşuluq matrisası oxunur. Qrafdakı kənarların sayı 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ə, və kənarları eyni sayılır. Bu səbəbdən dəyişəninin dəyəri -yə bölünür.
Edges //= 2
Dərinlik üzrə axtarış zamanı ziyarət edilmiş zirvələrin sayı dəyişənində saxlanılır.
c = 0
Dərinlik üzrə axtarışı -ci zirvədən başlat.
dfs(1)
Əgər qrafda kənar varsa və o əlaqəlidirsə , bu halda qraf ağacdır.
if Edges == n - 1 and c == n: print("YES") else: print("NO")
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ı və kənarlarının sayı verilir. Növbəti 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ış. ədəd kənar çap et — bu kənarlar ağacı təşkil etməlidir. Kənarların sırası önəmli deyil.

4 4 1 2 2 3 3 4 4 1
1 2 2 3 3 4
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ış -ci zirvədən başladılarsa, keçilən kənarlar belə olacaq: .
Qrafın qonşuluq matrisi massivində saxlanılır.
#define MAX 101 int g[MAX][MAX], used[MAX];
dfs funksiyası zirvəsindən başlayaraq dərinlik üzrə axtarış aparır. Dərinlik üzrə axtarış ağacına daxil olan hər bir 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); } }
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; }
Birinci zirvədən dərinlik üzrə axtarışa başlayın.
dfs(1);
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 zirvəli ağacdan maksimum 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 cütdür və — ağacdakı zirvə və kənarların sayı verilir. Sonrakı 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ü -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.

10 9 2 1 3 1 4 3 5 2 6 1 7 2 8 6 9 8 10 8
2
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 — -ci zirvədən başlayaraq hər bir zirvəsi üçün onun alt ağacında neçə zirvə olduğunu hesablayırıq. Əgər bu say cütdürsə, zirvəsini valideyninə birləşdirən kənar silinir.
Kök zirvə üçü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 çı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 və -nın alt ağacları cüt sayda zirvəyə malikdir. Zirvə kök olduğundan və onu valideynə birləşdirən kənar olmadığından, və kənarlarını silirik.
Ağac cüt sayda zirvəsi olan əlaqəli komponentə bölünüb.
Massivləri elan edirik.
#define MAX 101 int g[MAX][MAX], used[MAX];
dfs funksiyası zirvəsindən başlayaraq dərinlik üzrə axtarış aparır və kök olduqda onun alt ağacındakı zirvələrin sayını qaytarır.
int dfs(int v) { used[v] = 1;
dəyişənində zirvəsinin alt ağacındakı zirvələrin sayını hesablayırıq.
int s = 1;
zirvəsinin bütün qonşuları üzərində iterasiya aparırıq. Əgər zirvəsi istifadə olunmayıbsa, funksiyasını çağırıb nəticəni -ə ə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ü cütdürsə, və onun valideyni arasındakı kənar silinir.
if (s % 2 == 0) res++;
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ışı zirvəsindən başlayırıq. dəyişənində silinmiş kənarların sayını hesablayırıq.
res = 0; dfs(1);
Çünki zirvəsinin valideynə birləşdirən kənar yoxdur, nəticə olaraq çap olunur.
printf("%d\n", res - 1);
Python reallaşdırması
dfs funksiyası zirvəsindən başlayaraq dərinlik üzrə axtarış aparır və kök olduqda onun alt ağacındakı zirvələrin sayını qaytarır.
def dfs(v): used[v] = 1
dəyişənində zirvəsinin alt ağacındakı zirvələrin sayını hesablayırıq.
s = 1
Uşaqlar üzrə dövr qururuq. funksiyasının qaytardığı dəyəri -ə ə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ü cütdürsə, ilə onun valideyni arasındakı kənarı silirik.
if s % 2 == 0: global res res += 1
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
zirvəsindən başlayaraq dərinlik üzrə axtarış aparırıq. dəyişənində silinmiş kənarların sayını hesablayırıq.
res = 0 dfs(1)
Çünki zirvəsinin valideynə birləşdirən kənarı yoxdur, nəticə olaraq çap olunur.
print(res - 1)
Şirkətin strukturu verilir. Hər bir işçi üçün onun tabeliyində olan işçilərin sayını hesablayın.
Giriş. Birinci sətrdə — işçilərin sayı verilir. İşçilər , , ..., kimi nömrələnib və -ci işçi şirkətin baş direktoru sayılır.
Sonrakı sətrdə tam ədəd verilir: -ci, -cü, ..., -ci işçilərin hər biri üçün onların birbaşa rəhbərinin nömrəsi verilir.
Çıxış. tam ədəd çap edin: , , ..., -ci işçilərin hər biri üçün onun tabeliyində olan işçilərin sayı.

5 1 1 2 3
4 1 1 0 0
Şirkətin strukturu ağac şəklində verilib. Ağacdakı hər bir zirvəsi üçün — kök olan alt ağacda olan bütün zirvələrin sayını tapırıq. İşçi üçün onun tabeliyində olan işçilərin sayı olacaq (çünki özü öz tabeliyində sayılmır).
Dərinlik üzrə axtarışa -ci zirvədən — baş direktordan başlayırıq. Əgər zirvəsinin övladları -dırsa, onda
Əgər zirvəsi ağacın yarpaq nöqtəsidirsə, onda 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.
Ağac qonşuluq siyahısı ilə təqdim olunur.
vector<vector<int> > g;
dfs funksiyası zirvəsindən dərinlik üzrə axtarış həyata keçirir və zirvəsindən başlayan alt-ağacda olan qovşaqların sayını qaytarır. — dərinlik üzrə axtarışda zirvəsinin valideynidir.
int dfs(int v, int p) {
İlkin olaraq, zirvəsindən başlayan alt-ağac yalnız qovşağını ehtiva edir.
sum[v] = 1;
qırağını nəzərdən keçirək. Əgər -dirsə, onda dəyərinə 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);
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şçisinin birbaşa rəhbəri -dir.
g[i].push_back(x); g[x].push_back(i); }
Dərinlik üzrə axtarışı -ci zirvədən başlat.
sum.resize(n + 1); dfs(1, -1);
Cavabı çap et. işçisinin tabeliyində olanların sayı -ə 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(); } }
Python ilə reallaşdırma
Rekursiya üçün stekin ölçüsünü artırın.
import sys sys.setrecursionlimit(1000000)
dfs funksiyası qovşağından başlayaraq dərinlik-öncə axtarış aparır və qovşağında kök salan alt-ağacdakı qovşaq sayını qaytarır. isə qovşağının valideynidir.
def dfs(v, p):
Əvvəlcə, qovşağında kök salan alt-ağac yalnız özünü ehtiva edir.
sum[v] = 1
istiqamətli kənarını nəzərdən keçir. Əgər -dirsə, qovşağında kök salan alt-ağacdakı qovşaq sayını -ə əlavə et.
for to in g[v]: if to != p: sum[v] += dfs(to, 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ı 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]
-ci işçi üçün (), onun birbaşa rəhbəri -dır.
g[a].append(i) g[i].append(a)
siyahısını başladın.
sum = [0] * (n + 1)
Dərinlik-öncə axtarışı qovşağından başladın.
dfs(1, -1)
Cavabı çap edin. -ci işçinin tabeliyində olanların sayı -ə bərabərdir.
for i in range(1, n + 1): print(sum[i] - 1, end=" ")
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 — ağacdakı qovşaqların sayı verilir. Qovşaqlar -dən -ə qədər nömrələnib.
Sonrakı sətirin hər biri bir kənarı təsvir edir və və ədədlərindən ibarətdir, bu da və qovşaqları arasında bir kənarın olduğunu bildirir.
Çıxış. Bir tam ədəd çap edin — ağacın diametri.

5 1 2 1 3 3 4 3 5
3
Hər hansı bir zirvədən, məsələn, -ci zirvədən başlayaraq dərinlik üzrə axtarış (depth-first search) et. -dən ən uzaqda yerləşən zirvəni tap və onu kimi qeyd et. Sonra zirvəsindən dərinlik üzrə axtarışa başla və -dan ən uzaqda yerləşən zirvəni tap — onu kimi qeyd edək. Zirvələr və 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 -dür. Məsələn, maksimal məsafə və zirvələri arasında əldə edilir.
Tapşırıq
Ağacın diametrini tapın.
Daxil edilən ağacı qonşuluq siyahısı () kimi saxlayın. Mənbədən hər bir zirvəyə olan ən qısa məsafələri massivində saxlayın.
vector<vector<int>> g; vector<int> dist;
dfs funksiyası zirvə -dən dərinlik üzrə axtarış aparır. Mənbədən zirvə -yə olan ən qısa məsafə -dir. Zirvə -nin dərinlik üzrə axtarışdakı valideyni -dir.
void dfs(int v, int d, int p = -1) {
Mənbədən zirvə -yə olan ən qısa məsafəni -də saxlayın.
dist[v] = d;
Bütün kənarlarını iterasiya edin. Əgər zirvəsi -nin valideyni deyilsə, üçün dərinlik üzrə axtarış başladın. Mənbədən -ya olan məsafə 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); }
-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 kimi qeyd edin.
dfs(1, 0, -1); a = max_element(dist.begin(), dist.begin() + n + 1) – dist.begin();
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 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 ilə arasındakı ən qısa məsafəni.
printf("%d\n", dist[b]);
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 — ağacdakı zirvələrin sayı verilir. Zirvələr -dən -ə kimi nömrələnmişdir.
Növbəti sətrdə ağacdakı kənarlar təsvir olunur: hər sətrdə iki tam ədəd və verilir, bu da və zirvələri arasında bir kənar olduğunu göstərir.
Çıxış. ədəd çap edin. Hər bir -dən -ə qədər zirvə üçün, həmin zirvədən digər zirvələrə olan maksimal məsafəni çap edin.

5 1 2 1 3 3 4 3 5
2 3 2 3 3
İ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.
və — bir-birindən ən uzaqda yerləşən iki zirvədir — yəni ağacın diametrini təyin edən zirvələrdir.
zirvəsindən bütün digər zirvələrə qədər olan ən qısa məsafələri massivində, zirvəsindən olan məsafələri isə massivində saxlayırıq.
Sonra, hər bir zirvəsi üçün digər zirvələrə qədər maksimal məsafə belə hesablanır:
Nümunə
Misaldakı ağacı nəzərdən keçirək.
Ağacın diametri, məsələn, və zirvələri arasındakı yol ola bilər.
Girişdə verilən ağac qonşuluq siyahısı kimi saxlanılır.
və zirvələrindən olan ən qısa məsafələr müvafiq olaraq və massivlərində saxlanılır.
vector<vector<int>> g; vector<int> dista, distb;
dfs funksiyası zirvə -dən başlayaraq dərinlik üzrə axtarış aparır.
parametri — mənbədən zirvəsinə qədər olan məsafəni göstərir;
nəticələr massivində saxlanılır;
parametri — axtarış zamanı zirvəsinin valideyni.
void dfs(int v, int d, vector<int>& dist, int p = -1) {
Mənbədən zirvəsinə qədər olan məsafəni dəyişənində saxla:
dist[v] = d;
Bütün kənarları üzrə dövr qur. Əgər — zirvəsinin valideyni deyilsə, o zaman həmin 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ə -dən ən qısa məsafələri hesablayırıq. Ondan ən uzaqda yerləşən zirvə -dır.
dfs(1, 0, dista, -1); a = max_element(dista.begin() + 1, dista.begin() + n + 1) - dista.begin();
Daha sonra, zirvəsindən ən qısa məsafələri hesablayırıq və massivində saxlayırıq. zirvəsindən ən uzaq olan zirvə olacaq. və 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, zirvəsindən ən qısa məsafələri hesablayırıq və massivində saxlayırıq.
dfs(b, 0, distb, -1);
Hər bir 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ə -dən başlayaraq dərinlik üzrə axtarış (DFS) yerinə yetirir.
parametri başlanğıc nöqtədən zirvəsinə olan ən qısa məsafəni göstərir.
Nəticələr massivində saxlanılır.
parametri zirvəsinin DFS zamanı valideynini göstərir.
def dfs(v, d, dist, p = -1):
Başlanğıc nöqtədən zirvəsinə olan ən qısa məsafəni -də saxla.
dist[v] = d
Bütün kənarlarını nəzərdən keçirt. Əgər -nin valideyni deyilsə, bu halda 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 -ci zirvədən hesablayın. Ondan ən uzaqda yerləşən zirvə -dır.
dfs(1, 0, dista) a = dista.index(max(dista[1:]))
Daha sonra zirvəsindən ən qısa məsafələri hesablayın və nəticələri massivində saxlayın. -dan ən uzaqda yerləşən zirvə -dir. və zirvələri arasındakı məsafə ağacın diametri olacaq.
dfs(a, 0, dista) b = dista.index(max(dista[1:]))
Daha sonra zirvəsindən ən qısa məsafələri hesablayın və nəticələri massivində saxlayın.
dfs(b, 0, distb)
Hər bir 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)
Romanın valideynləri ona zirvəli və 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 zirvəsindən zirvəsinə olan yol ilə zirvəsindən 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 — qrafdakı zirvələrin sayı verilir.
Növbəti 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 -də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 -a görə modul ilə verin.

3 1 2 1 1 3 3
8
6 1 2 5 1 3 1 2 4 2 2 5 4 2 6 3
90
Ağacın istənilən zirvəsindən başlayaraq dərinlik üzrə axtarış (DFS) aparın. çəkisi olan bir qəlpəni nəzərdən keçirək. Əgər zirvəsi kök olan altqrafda sayda zirvə varsa, o zaman bu qəlpənin bir tərəfində , digər tərəfində isə zirvə yerləşir. Bu halda qəlpəsi 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 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.
qəlpəsi (çəki ) aşağıdakı 2 yolda iştirak edir:
;
;
Bu qəlpənin ümumi cəmi töhfəsi .
qəlpəsi (çəki ) aşağıdakı 2 yolda iştirak edir:
;
;
Bu qəlpənin ümumi cəmi töhfəsi .
Yolların ümumi uzunluğu .
İkinci nümunədə qəlpəsinin (çəki ) töhfəsini hesablayın.
zirvəsi kök olan altqrafda zirvə var (özünü də daxil olmaqla). Digər tərəfdə isə zirvə var. Buna görə, qəlpəsi fərqli yolda iştirak edir.
Həqiqətən, bunlar aşağıdakı cütlərdir:
Bu qəlpənin ümumi uzunluğa verdiyi töhfə:
Modul dəyərini kimi təyin edin.
#define MOD 1000000000
Girişdə verilən çəkili qraf qonşuluq siyahısı şəklində saxlanılır.
vector<vector<pair<int,int> > > g;
dfs funksiyası zirvəsindən başlayaraq dərinlik üzrə axtarış aparır və zirvəsi kök olan altqrafdakı zirvələrin sayını qaytarır (özünü də daxil olmaqla). Bu cəmi dəyişənində saxlayırıq. Əvvəlcə götürülür, çünki altqrafda artıq 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;
qəlpəsini və onun çəkisi -nu nəzərdən keçirək. zirvəsi kök olan altqrafda sayda zirvə varsa, bu zaman qəlpənin bir tərəfində , digər tərəfində isə zirvə yerləşir. qəlpəsi 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ə:
if (to != p) { int c = dfs(to, v); res = (res + 1LL * c * (n - c) * w) % MOD; cnt += c; } } return cnt; }
Ə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}); }
Dərinlik üzrə axtarışı -ci zirvədən başlayın. Qrafın zirvələri -də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()); } }
Python tətbiqi
Modul dəyərini kimi təyin edin.
MOD = 1000000000
dfs funksiyası qovşağından başlayaraq dərinlik üzrə axtarış (DFS) aparır və qovşağında köklənmiş altqrafda yerləşən qovşaqların sayını qaytarır (özünü də daxil olmaqla). Bu say dəyişəni ilə hesablanır. Başlanğıcda qoyulur, çünki altqraf artıq qovşağını ehtiva edir.
def dfs(v, p = -1): global res cnt = 1 for to, w in g[v]:
çəkili kənarını nəzərdən keçirək. Qoy qovşağında köklənmiş altqrafda qovşaq olsun. Onda bu kənarın bir tərəfində , digər tərəfində isə qovşaq yerləşir. kənarı 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:
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))
Dərinlik üzrə axtarışı -ci qovşaqdan başlayın. Qrafın qovşaqları -dən -ə kimi nömrələnib.
dfs(1)
Cavabı çap edin.
print(res)
qovşaqdan ibarət köklü bir ağac verilir. Hər bir qovşaq müxtəlif rəngdən biri ilə rənglənib. Hər bir 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 verilir. Növbəti sətir ağacın qovşaqlarını təsvir edir. -ci sətirdə iki tam ədəd və verilir, burada — -ci qovşağın valideyni, isə onun rəngidir . Ağacın kökü üçün .
Çıxış. tam ədəd çap edin — -dən -ə qədər hər bir qovşaq üçün, onun altqrafında neçə müxtəlif rəngin olduğunu göstərin.

5 2 1 3 2 0 3 3 3 2 1
1 2 3 1 1
Ağacın kökündən başlayaraq dərinlik üzrə keçid (DFS) yerinə yetirin. Hər bir təpəsi üçün ç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ı təpəsi təpəsinin övladıdırsa, çoxluğu çoxluğuna birləşdirilməlidir.
Beləliklə, təpəsinin altqrafında olan fərqli rənglərin sayı ç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.
massivi təpəsinin rəngini saxlayır. çoxluğu təpəsinin altqrafında olan rəngləri toplayır.
İstiqamətli ağac qonşuluq siyahısı kimi massivində saxlanılır. isə 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 təpəsindən başlayaraq həyata keçirir.
void dfs(int v) {
İlk olaraq, təpəsinin öz rəngi çoxluğuna əlavə olunur.
s[v].insert(color[v]);
Ağacın istiqamətli kənarları üzrə iterasiya aparılır.
for (int to : g[v]) { dfs(to);
Hər kənarı üçün çoxluğu çoxluğuna birləşdirilir. Əgər çoxluğu çoxluğundan kiçikdirsə, əvvəlcə onlar yer dəyişdirilir. Sonra isə çoxluğunun bütün elementləri ç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());
çoxluğu təmizlənir — artıq ona ehtiyac yoxdur.
s[to].clear(); }
Bundan sonra, dəyişəninə çoxluğunun ölçüsü yazılır — bu da 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; }
Ağacın kökündən — yəni 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");
Mançester və Liverpul sakinləri uzun və yorucu iş həftəsindən sonra istirahət üçün meşəyə yollanırlar. Orada onlar təpədən ibarət olan və hər təpəsi 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ü 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 və — ağacdakı təpələrin və mümkün rənglərin sayı verilir.
İkinci sət ədəddən ibarətdir: bunların -cisi -ci təpənin valideynini göstərir.
Üçüncü sət ədəddən ibarətdir — təpələrin rəngləri. Hər rəng ilə arasında olan bir tam ədəddir.
Çıxış. Bir sətrə 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 yazın.
5 4 1 1 3 3 1 4 2 1 2
-1 -1 -1 1 3
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 -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. 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 ( təpəsi) başlayın. Rəngi olan təpəsi üçün aşağıdakı addımlar icra olunur:
Əgər steki boş deyilsə, onun yuxarısında olan təpə vertex ilə eyni rəngə malik ən yaxın əcdaddır. Əgər stek boşdursa, belə bir əcdad yoxdur, və cavab olacaq.
təpəsini stekinə əlavə edin.
Rekursiv olaraq vertex -nin bütün övladları üçün DFS tətbiq edin.
Vertex üzrə işləmə bitdikdən sonra onu stekindən çıxarın.
DFS zamanı biz təpəsində olanda, steklər kökdən 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ə, steki bu yolda rəngi 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 təpəsinə çatanda stekdən ikisi (rəngləri və olanlara uyğun olanlar) boş olacaq.
Rəngi olan stek (1 nömrəli stek) təpəsini, rəngi olan stek (2 nömrəli stek) isə təpəsini saxlayacaq. Çünki təpəsinin rəngi -dir, onunla eyni rəngə malik ən yaxın əcdadı nömrəli stekin yuxarısındakı təpə olacaq.
Beləliklə, təpəsinin eyni rəngə malik ən yaxın əcdadı -dür.
Massivləri elan edin.
vector<int> col, res; vector<vector<int> > g; vector<stack<int> > s;
dfs funksiyası təpəsindən başlayaraq dərinlik üzrə axtarış aparır.
void dfs(int v) {
təpəsinin rəngi dəyişənində saxlanılır.
int color = col[v];
təpəsi ilə eyni rəngə malik ən yaxın əcdadın nömrəsi dəyişənində saxlanılır.
if(s[color].empty()) res[v] = -1; else res[v] = s[color].top();
təpəsini onun rənginə uyğun olan stekə əlavə edin.
s[color].push(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);
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); }
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 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ı təpəsindən başlayaraq dərinliyinə doğru axtarış (DFS) yerinə yetirir.
def dfs(v):
təpəsinin rəngi dəyişəndə dəyişənində saxlanılır.
color = col[v]
təpəsi ilə eyni rəngdə olan ən yaxın ata təpənin nömrəsi dəyişənində saxlanılır.
if not s[color]: res[v] = -1 else: res[v] = s[color][-1]
təpəsi öz rənginə uyğun olan stekə əlavə olunur.
s[color].append(v)
təpəsinin bütün övladları üçün DFS rekursiv şəkildə çağırılır.
for to in g[v]: dfs(to)
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ışı 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:])
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 video yükləmişlər, və bu videolar -də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ə 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 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 dəyərini seçmək istəyir ki, MooTube-da olan istənilən video üçün uyğunluğu azı 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 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 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 olsa, videoya neçə digər video tövsiyə olunacağını müəyyənləşdirməlisiniz.
Giriş. Birinci sətirdə iki tam ədəd və — video sayı və sorğuların sayı verilir.
Növbəti sətirdə Farmer John tərəfindən qiymətləndirilmiş video cütləri verilir. Hər sətirdə üç tam ədəd və , bu o deməkdir ki, və videoları uyğunluq dəyəri olan kənarla birləşdirilmişdir.
Sonra isə sorğu verilir. -ci sorğuda iki tam ədəd və verilir — bu o deməkdir ki, Farmer John soruşur: əgər minimum uyğunluq olarsa, videosu üçün neçə video tövsiyə ediləcək?
Çıxış. sətir çap edin. -ci sətirdə Farmer John-un -ci sorğusunun cavabını verin.

Nümunə. Farmer John videolar arasında aşağıdakı əlaqələri qurmuşdur:
Video və video arasında uyğunluq -dür,
Video və video arasında uyğunluq -dir,
Video və video arasında uyğunluq -dür.
Bu məlumatlara əsasən, digər video cütləri arasındakı uyğunluqları hesablaya bilərik:
Video və video : uyğunluq = ,
Video və video : uyğunluq = ,
Video və video : uyğunluq = .
İndi isə aşağıdakı sorğular üçün hansı videoların tövsiyə olunacağını nəzərdən keçirək:
Video üçün olduqda, videolar , və tövsiyə olunacaq.
Video üçün olduqda, videolar və tövsiyə olunacaq.
Video üçün olduqda, heç bir video tövsiyə olunmayacaq.
4 3 1 2 3 2 3 2 2 4 4 1 2 4 1 3 1
3 0 2
Hər bir sorğusu üçün təpəsindən başlayaraq DFS və ya BFS həyata keçirin. Axtarış zamanı yalnız uyğunluğu və ya ondan böyük olan kənarları keçin.
Əldə edilə bilən təpələrin sayını kimi hesablayın, başlanğıc təpə nəzərə alınmadan. dəyəri sorğusunun cavabıdır.
Nümunə
Farmer John videolar arasında aşağıdakı əlaqələri qurmuşdur:
Video və video arasında uyğunluq -dür,
Video və video arasında uyğunluq -dir,
Video və video arasında uyğunluq -dür.
Bu məlumatlara əsaslanaraq digər video cütləri arasındakı uyğunluğu hesablaya bilərik:
Video və video : uyğunluq = ,
Video və video : uyğunluq = ,
Video və video : uyğunluq = .
İndi aşağıdakı sorğular üçün hansı videoların tövsiyə olunacağını baxaq:
üçün video -dən: videolar , və tövsiyə olunur.
üçün video -dən: videolar və tövsiyə olunur.
üçün video -dən: heç bir video tövsiyə olunmur.
Girişi verilən qrafı qonşuluq siyahısı şəklində saxlayın.
vector<vector<pair<int, int>>> g;
bfs funksiyası başlanğıc təpədən () 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 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 təpəsi ziyarət olunubsa, təyin olunur.
vector<int> used(n + 1); used[start] = 1;
Bir növbə yaradılır və başlanğıc təpə ora əlavə olunur.
queue<int> q; q.push(start);
Nəticə dəyişəni 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ə çıxarılır.
int v = q.front(); q.pop();
Bütün təpəsinə bitişik kənarlar üzrə iterasiya aparılır.
for (auto edge : g[v]) {
kənarını və onun çəkisini () götür.
int to = edge.first; int kto = edge.second;
Əgər kənarın çəkisi olarsa, bu kənar atlanır (bu kənarla hərəkət qadağandır).
if (kto < k) continue;
Əgər təpəsi hələ ziyarət olunmayıbsa, onu növbəyə əlavə et, ziyarət olunmuş kimi işarələ və sayğacını artır.
if (used[to] == 0) { q.push(to); used[to] = 1; res++; } } }
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);
kənarını çəkisi olmaqla yönsüz şəkildə qonşuluq siyahısına əlavə et.
g[p].push_back({ q, r }); g[q].push_back({ p, r }); }
sorğularını bir-bir emal et.
while (qu--) { scanf("%d %d", &k, &v);
Təpə -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ı kimi saxlanılır.
vector<vector<pair<int, int>>> g;
dfs funksiyası 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 və ya ondan böyük olan kənarlarla hərəkət etmək icazəlidir.
int dfs(int v, int k) {
təpəsini ziyarət olunmuş kimi işarələ.
used[v] = 1;
təpəsindən başlayan altqrafda ziyarət olunan təpələrin sayı dəyişənində saxlanılır.
int res = 1;
təpəsinə bitişik bütün kənarları dövr ilə yoxla.
for (auto edge : g[v]) {
kənarını və onun çəkisini götür.
int to = edge.first; int kto = edge.second;
Əgər olarsa, bu kənar atlanır (bu kənarla hərəkət qadağandır).
if (kto < k) continue;
Əgər təpəsi hələ ziyarət olunmayıbsa, təpəsindən DFS başlat və nəticəni 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 olan istiqamətsiz kənarını qonşuluq siyahısına əlavə et.
g[p].push_back({ q, r }); g[q].push_back({ p, r }); }
sorğularını bir-bir emal et.
while (qu--) { scanf("%d %d", &k, &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ə daxil edilmədən.
used.assign(n + 1, 0); printf("%d\n", dfs(v, k) - 1); }
təpəli ağac verilib. Ağacın kənarlarının çəkiləri yalnız və ya 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ə — qrafın təpələrinin sayı verilir. Sonrakı sətrdə kənarların təsviri verilir. Hər sətrdə tam ədəd verilir: kənarla birləşən təpələrin nömrələri (təpələr -dən -ə kimi nömrələnib) və bu kənarın çəkisi ( və ya ).
Çıxış. Bütün təpə cütləri üçün XOR cəmlərinin ümumi cəmini çap edin.

5 1 2 1 2 3 1 2 4 0 4 5 1
6
Dərinlik üzrə axtarış (DFS) istifadə edərək, təpəsi ilə bütün digər təpələr arasındakı XOR cəmlərini hesablayın. və təpələri arasındakı XOR cəmini massivində saxlayın. massivində neçə olduğunu , neçə olduğunu isə kimi təyin edək (). O zaman cavab olacaq.
Nümunə
Nümunədə verilmiş qrafı nəzərdən keçirək.
Hər təpənin yanında ilə həmin təpə arasındakı XOR cəmi göstərilib. Əgər bəzi və təpələri üçün olarsa, o zaman bu iki təpə arasında XOR cəmi olacaq və bu cütlük ümumi cəmə töhfə verməyəcək. Əgər olarsa, bu cüt XOR cəminə töhfə verəcək.
Beləliklə, cavab olan bütün cütlüklərin sayına bərabərdir. Bu say olacaq. XOR cəminə töhfə verən cütlüklər bunlardır: .
Girişi qonşuluq siyahısı şəklində saxlayın. massivini elan edin.
vector<vector<pair<int,int> > > g; vector<int> x;
dfs funksiyası dərinlik üzrə axtarış həyata keçirir və və təpələri arasındakı XOR cəmi massivində saxlanılır. Hal-hazırkı XOR cəmi , təpəsinin valideyni isə 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); } }
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}); }
Dərinlik üzrə axtarışı təpəsindən başlayın.
dfs(1, 0, -1);
massivində və 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ı və təpələri arasındakı XOR cəmini hesablayan dərinlik üzrə axtarış həyata keçirir. və arasındakı cari XOR cəmi ilə göstərilir. təpəsinin valideyni -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))
massivini başlat.
x = [0] * (n + 1)
Dərinlik üzrə axtarışı təpə -dən başlayın.
dfs(1)
massivindəki və sayını hesablayın.
ones = sum(x) zeroes = n - ones
Cavabı ekrana verin.
print(ones * zeroes)
-dən -ə qədər nömrələnmiş təpədən ibarət bir ağac verilir. Ağacdakı -ci kənar və təpələrini birləşdirir. -ci təpə rəngi ilə boyanıb (bu məsələdə rənglər tam ədədlərlə təmsil olunur).
Bir təpə yaxşı hesab olunur əgər təpə -dən təpə -ə qədər olan ən qısa yol üzərində -dən başqa elə bir təpə yoxdur ki, ilə eyni rəngdə olsun.
Bütün yaxşı təpələri tapın.
Giriş. Birinci sətirdə bir tam ədəd — təpələrin sayı verilir.
İkinci sətirdə tam ədəd verilir — təpələrin rəngləri: .
Növbəti sətrin hər birində iki tam ədəd və 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.

6 2 7 1 8 2 8 1 2 3 6 3 2 4 3 2 5
1 2 3 4 6
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
1 2 3 5 6 7 8
Təpə -dən başlayaraq dərinlikə doğru axtarış (DFS) həyata keçirin. Rəngi olan təpəsinə daxil olduqda, dəyişəninin dəyərini vahid artırın. dəyəri, təpəsindən 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ə, təpəsi "yaxşı" hesab olunur və nəticə çoxluğuna əlavə olunur.
Təpədən çıxarkən dəyişəninin dəyərini vahid azaldın.
Nümunə
Birinci nümunədəki qraf aşağıdakı kimidir:
Təpə "yaxşı" deyil, çünki yolunda həm , həm də təpələri eyni rəngdədir.
Təpə isə "yaxşı"dır, çünki yolunda ara təpələrin heç biri ilə eyni rəngə malik deyil.
Giriş qrafını qonşuluq siyahısı () 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. dəyişəni təpəsinin valideynidir.
void dfs(int v, int par) {
Təpə -nin rəngi -dir. Bu rəngin yolda göründüyünü qeyd edin:
used[color[v]]++;
Əgər rəngi yolda yalnız bir dəfə görünürsə, təpəsini nəticə çoxluğuna əlavə edin:
if (used[color[v]] == 1) st.insert(v);
Təpə -dən çıxan bütün kənarları üzrə iterasiya aparın. Əgər təpəsi -nin valideyni deyilsə , ondan dərinliyə doğru axtarışa başlayın. Bu halda -nun valideyni olur.
for (int to : g[v]) if (to != par) dfs(to, v);
Təpə -dən çıxarkən, dəyərini vahid azaldın.
used[color[v]]--; }
Proqramın əsas hissəsi. Təpələrin sayı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); }
Təpə -dən dərinlik üzrə axtarışa başlayın.
dfs(1, 1);
Yaxşı təpələri çap edin. Onlar çoxluğunda saxlanılır.
for (int val : st) printf("%d\n", val);
-dən -ə qədər nömrələnmiş təpə və kənardan ibarət bir ağac verilir. -ci kənar və təpələrini birləşdirir.
Sizin ixtiyarınızda rəng var. Ağacın hər təpəsini bu rəngdən biri ilə rəngləməlisiniz, aşağıdakı şərti ödəməklə:
Əgər iki fərqli təpə və arasında olan məsafə və ya daha azdırsa, o zaman və fərqli rənglərə malik olmalıdır.
Ağacı bu şərti pozmadan neçə fərqli şəkildə rəngləmək olar? Cavabı modulu üzrə hesablayın.
Giriş. Birinci sətirdə iki tam ədəd və verilir. Növbəti sətrin hər birində iki tam ədəd və — ağacın kənarları verilir.
Çıxış. Ağacı verilmiş qayda ilə neçə müxtəlif şəkildə rəngləmək olar? Cavabı modulu üzrə çap edin.
4 3 1 2 2 3 3 4
6
5 4 1 2 1 3 1 4 4 5
48
Rəngləməyə təpə -dən başlayaq. Bu təpə fərqli rənglə rənglənə bilər.
Tutaq ki, biz təpəsindəyik. Əgər onun valideyni yoxdursa (), onun övladları fərqli rənglə rənglənə bilər. Əgər -nin valideyni varsa, o zaman övladları fərqli rənglə rənglənə bilər. Çünki -nin övladlarının arasındakı məsafə -ə bərabərdir və bu səbəbdən onlar eyni rəngdə ola bilməzlər.
Əgər -nin valideyni varsa, o zaman onun birinci övladı , ikinci övladı 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 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, təpəsi yalnız rənglə rənglənə bilər, çünki onun rəngi və təpələrinin rəngindən fərqli olmalıdır. Ağacı rəngləməyin ümumi yolları
Girişi təmsil edən qrafı qonşuluq siyahısı şəklində yadda saxlayın. Modul üçün sabit təyin edin.
#define MOD 1000000007 vector<vector<int>> g;
dfs funksiyası təpəsi ilə köklənmiş ağacı modulu üzrə neçə yolla rəngləmək mümkün olduğunu qaytarır. təpəsinin valideyni , özü isə rənglə rənglənə bilər.
int dfs(int v, int col, int p = -1) { long long res = col;
Hazırda təpəsindəyik. dəyişənində təpəsinin övladları üçün istifadə oluna bilməyəcək rənglərin sayı saxlanılır. Başlanğıcda təyin edilir, çünki övladlar -nin rəngini ala bilməzlər. Əgər -nin valideyni varsa (), o zaman övladlar həmçinin valideynin rəngini də ala bilməz.
int cnt = 1; if (p >= 0) cnt++;
təpəsinin bütün övladları üzrə iterasiya aparılır.
for (int to : g[v]) if (to != p) {
təpəsi fərqli rənglə rənglənə bilər. Hər bir övladdan sonra dəyişəni 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); }
Dərinlik üzrə axtarışı təpə -dən başladın. nömrəli təpə 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 təyin edin.
MOD = 1000000007
dfs funksiyası təpəsi ilə köklənmiş ağacı modulu üzrə neçə yolla rəngləmək mümkün olduğunu qaytarır. təpəsinin valideyni -dir. təpəsi qədər fərqli rənglə rənglənə bilər.
def dfs(v, col, p = -1): res = col
Hazırda təpəsindəyik. dəyişənində təpəsinin övladları üçün istifadə oluna bilməyəcək rənglərin sayı saxlanılır. Başlanğıcda təyin edilir, çünki övladlar təpəsinin rəngini ala bilməzlər. Əgər təpəsinin valideyni varsa (), o zaman övladlar həmçinin valideynin rəngini də ala bilməzlər.
cnt = 1 if p >= 0: cnt += 1
təpəsinin bütün övladları üzrə iterasiya aparılır.
for to in g[v]: if to != p:
təpəsi fərqli rənglə rənglənə bilər. Hər bir övladdan sonra dəyişəni 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ə -dən başladın. nömrəli təpə rəngdən biri ilə rənglənə bilər.
res = dfs(1, k)
Cavabı çap edin.
print(res)
Ölkədə şəhər və 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 -də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 -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 modulu üzrə çap edin.
Giriş. Birinci sətirdə bir tam ədəd — ölkədəki şəhərlərin sayı verilir. İkinci sətirdə ədəd ədədləri verilir. Burada ədədi və şə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ı modulu üzrə çap edin.

3 1 1
4
6 1 2 2 1 5
15
— təpəsi ilə köklənmiş altqraf üçün yolları yaxşılaşdırmağın üsullarının sayını göstərsin. — təpəsinin övladıdır.
Əgər yolu pis vəziyyətdə qalırsa, o zaman təpəsinin altqrafındakı bütün yollar yaxşılaşdırılmalıdır.
Əgər yolu yaxşılaşdırılırsa, o zaman təpəsinin altqrafı üçün yolları yaxşılaşdırmağın sayı -ya bərabərdir.
Əgər — təpəsinin övladlarıdırsa, o zaman
Əgər yarpaq təpədirsə (övladı yoxdursa), onda qəbul edilir.
Nümunə
Məsələdə verilmiş nümunələrə baxaq. Hər təpə üçün qiymətini qeyd edək.
Birinci nümunədə yolları elə yaxşılaşdırmağın üsulu var ki, şəhər -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.
Bütün hesablamalar moduluna görə aparılır.
#define MOD 1000000007
dfs funksiyası qiymətini hesablayır — təpəsindən başlayan altqraf üçün yolları yaxşılaşdırmağın üsullarının sayı. təpəsinin valideyni -dir.
long long dfs(int v, int p = -1) { long long s = 1;
Əgər — təpəsinin övladlarıdırsa, o zaman
for (int to : g[v]) if (to != p) { long long sub = dfs(to, v); s = (s * (sub + 1)) % MOD; } return s; }
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); }
Nəticəni hesablayın və çap edin.
res = dfs(1); printf("%lld\n", res);
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 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 təpə və qırıqdan ibarət bir ağacdır.
Labirintin girişi -ci otaqdadır. Bir otaq yalnız bir başqa otaqla birləşibsə və bu otaq kök deyilsə (yəni -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 -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 — labirintdəki otaqların sayı verilir.
Növbəti sətirin hər birində iki tam ədəd 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ı.
3 1 2 1 3
2
4 1 2 2 3 2 4
1
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:
— təpə -dən təpə -yə olan məsafə. Əgər təpə -nin valideynidirsə, onda
— təpə -dən öz altqrafında yerləşən ən yaxın yarpaq təpəyə olan məsafə. Əgər — təpəsinin övladlarıdırsa, onda
Əgər ağacın yarpaq təpəsidirsə, o zaman təyin edin.
Sonra ikinci DFS keçidi edin. Bu keçid zamanı qiymətini hesablayın — əgər kəpənək bu altqrafa daxil olarsa, onu tutmaq üçün təpəsinin altqrafındakı yarpaqlarda yerləşdirilməli olan dostların minimal sayı.
Əgər , onda . Bu halda 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 təpəsinə çatacaq.
Əks halda, əgər — təpəsinin övladlarıdırsa, onda
Əgər kəpənək təpəsində tutulmayıbsa, onu övladlarının altqraflarında tutmaq üçün hazır olmalıyıq.
Problemin son cavabı 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 -ci təpədən ya -yə, ya da -ə 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 , ya da təpəsində yerləşə bilər. Kəpənək bir dəqiqədə -dən -yə uçar, həmin vaxtda dost da -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.
Sonsuzluq sabitini və massivləri təyin edin.
#define INF 2000000000 vector<vector<int>> g; vector<int> d, h, res;
dfs funksiyası təpə -dən başlayaraq dərinlik üzrə ilk keçidi (DFS) yerinə yetirir və qiymətini qaytarır. Burada təpə -nin valideyni, isə təpə -dən -yə olan məsafəni göstərir. Keçid zamanı hər bir təpəsi üçün və qiymətləri hesablanır.
int dfs(int v, int p = -1, int cnt = 0) {
dəyişəni, yəni -dən -yə olan məsafə, -də saxlanılır.
d[v] = cnt; int height = INF;
Təpə -nin bütün övladlarını keçin. qırağını nəzərdən keçirin. Əgər -dirsə, bu halda keçidi atlayın.
for (int to : g[v]) if (to != p)
dəyişənində aşağıdakı qiymət hesablanır:
burada təpə -nin övladlarıdır.
Əgər -dirsə, onda təpəsi yarpaqdır və təyin edilir. Əks halda, qaytarılan qiymət:
return h[v] = (height == INF) ? 0 : 1 + height; }
dfs2 funksiyası təpə -dən başlayaraq DFS yerinə yetirir və qiymətini qaytarır. Burada təpə -nin valideynidir.
int dfs2(int v, int p = -1) { int s = 0;
— təpəsinin övladlarıdır. dəyişənində aşağıdakı cəmi hesablayın:
for (int to : g[v]) if (to != p) { dfs2(to, v); s += res[to]; }
Əgər -dirsə, bir dost kifayətdir və olacaq. Əks halda
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); }
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 təpəsi üçün 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]);
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 təpədən ibarət köklü bir ağacdır və kök -ci təpədir. Kefa-nın evi də -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 -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: və — 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ə tam ədəd verilir: , burada hər bir ya (təpədə pişik yoxdur), ya da (təpədə pişik var) olur.
Sonrakı sətirdə ağacın qıraqları formatında verilir, burada və 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 -dən artıq ardıcıl pişik olmayan yarpaq təpələrə çatmaq imkanlarının sayını çap edin.

7 1 1 1 0 0 0 0 1 1 2 1 3 2 4 2 5 3 6 3 7
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
2
Kefa-nın evinin yerləşdiyi -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ə -də bu say -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 -dir və o, kök təpə (yəni ) 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, və -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, və -ci təpələrdəki restoranlara çata bilər.
Pişiklərin yerləşdiyi məlumatlar massivində saxlanılır. Ağac isə bitişik siyahı şəklində massivində saxlanılır.
vector<int> cats; vector<vector<int> > g;
dfs funksiyası 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ı -dən çox olmasın). — təpəsinin valideynidir. isə 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 -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;
dəyişənində təpəsindən çatmaq mümkün olan restoranların sayı toplanır.
int res = 0;
təpəsindən çıxan bütün qıraqlarını gəz. Əgər -nin valideynidirsə, bu təpəyə keçmə.
for (int to : g[v]) if (to != prev) {
Əgər təpəsində pişik yoxdursa, təyin et. Əks halda . dəyişəni 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;
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); }
Nəticəni çap et. dfs funksiyası təpəsindən çağırılır və məsələnin cavabını qaytarır. Çünki təpəsinin valideyni yoxdur, ikinci parametr kimi ötürülür. təpəsindən başladıqda, artıq qədər pişiklə qarşılaşmışıq.
printf("%d\n", dfs(1, -1, cats[1]));
Python reallaşdırılması
dfs funksiyası 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ı -dən çox olmasın). — təpəsinin valideynidir. isə 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 -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
dəyişənində təpəsindən çatmaq mümkün olan restoranların sayı toplanır.
res = 0
təpəsindən çıxan bütün qıraqlarını gəz. Əgər -nin valideynidirsə, bu təpəyə keçmə.
for to in g[v]: if to != prev:
Əgər təpəsində pişik yoxdursa, təyin et. Əks halda . dəyişəni 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
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ı təpəsindən çağırılır və məsələnin cavabını qaytarır. Çünki təpəsinin valideyni yoxdur, ikinci parametr kimi ötürülür. təpəsindən başladıqda, artıq qədər pişiklə qarşılaşmışıq.
print(dfs(1, -1, cats[1]))
-dən -ə qədər nömrələnmiş təpəli bir ağac verilir. təpəsi kök kimi qəbul olunur. Hər iki təpə arasında yalnız bir sadə yol mövcuddur.
— təpəsindən təpəsinə qədər olan yoldakı qıraq (kənar) sayıdır.
Elə cütlüklərinin sayını tapın ki, aşağıdakı bərabərlik ödənmiş olsun:
Giriş. Birinci sətirdə bir tam ədəd — ağacdakı təpələrin sayı verilir.
Sonrakı sətrin hər birində ağacdakı qıraqlar təsvir olunur: hər biri iki tam ədəddən ibarət olan cütü.
Çıxış. bərabərliyini ödəyən cütlüklərinin sayını çap et.
5 1 2 2 3 2 4 4 5
13
şərti yalnız və yalnız təpəsi təpəsinin DFS-də (dərinlik üzrə axtarış) əcdadı olduqda doğrudur.
Gəlin bir nümunəyə baxaq:
, ;
, ;
, ;
, ;
Gəlin ilə təpə -nin dərinliyini təyin edək — bu, kökdən ( təpəsindən) -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 təpəsini sabit saxlayaraq soruşaq: neçə təpəsi var ki, bərabərliyi ödənir?
Məsələn, üçün belə təpə var: . Burada . Deməli, sabit bir üçün sayda uyğun mövcuddur.
Buna görə də məsələni həll etmək üçün hər bir təpə üçün 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: .
Qrafın qonşuluq siyahısını kimi müəyyən edək. Təpələrin dərinlikləri massivində saxlanılır.
vector<vector<int> > g; vector<int> h;
dfs funksiyası hər bir təpənin dərinliyini hesablayır. təpəsinin valideyni təpəsidir.
int dfs(int v, int p) { for (int to : g[v])
qırağını nəzərdən keçir. Əgər təpəsi -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]; }
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); }
təpəsinin dərinliyini olaraq təyin et.
h.resize(n + 1); h[1] = 1;
DFS-i 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);
İkiyollu bir ağac verilib, yəni təpə və qıraqlı, dövrə olmayan və əlaqəli qraf. Hər bir təpənin dərəcəsi maksimum -dür. Kök təpə -dir və onun dərəcəsi -dən artıq deyil.
Təəssüf ki, kök təpəsi yoluxub. Aşağıdakı proses 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ı verilir.
Növbəti sətirdə və cütlükləri verilir — bu, ağacdakı bir qırağı təsvir edir.
Zəmanət verilir ki, verilmiş qraf kökü olan iki yollu ağacdır.
Çıxış. Xilas edilə biləcək maksimum təpələrin sayını verin.
4 1 2 2 3 2 4
2
7 1 2 1 5 2 3 2 4 5 6 5 7
2
Hər bir təpəsi üçün onun kök olduğu altqrafdakı təpələrin sayını kimi hesablayırıq. Əgər — təpəsinin övladlarıdırsa, o zaman
Əgər təpəsi yarpaqdırsa, onda .
— kökü olan altqrafda yoluxmadan xilas edilə biləcək təpələrin sayıdır (əgər yalnız təpəsi yoluxubsa).
Əgər təpəsinin yalnız bir övladı varsa, onda (çünki silinir və xilas olunmuş sayılmır).
Əgər təpəsinin iki övladı var — və , o zaman iki variant var:
təpəsini silmək və altqrafındakı təpələri xilas etmək: .
təpəsini silmək və altqrafındakı təpələri xilas etmək: .
Bu iki variantdan maksimumunu seçirik.
DFS zamanı qırağını keçərkən aşağıdakı şəkildəki vəziyyəti nəzərdən keçirək:
təpəsinin ikinci övladı -dır. Biz ifadəsini hesablamaq istəyirik. Bunun üçün yalnız və təpələrindən istifadə edirik. Aşağıdakı bərabərlik doğrudur:
Bundan belə nəticə çıxır ki:
Bu halda,
Beləliklə, təpə üçün övladları üzərində iterasiya edib aşağıdakı maksimumu tapırıq:
Nümunə
Birinci nümunədə Huseyn -ci təpəni ilk gediş olaraq silməklə və təpələrini xilas edə bilər.
İkinci nümunəyə baxaq. Hər təpənin yanında 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,
Massivləri elan edək:
#define MAX 300005 int sum[MAX], dp[MAX]; vector<vector<int>> g;
dfs funksiyası təpə -dən başlayaraq DFS icra edir. — təpəsinin valideynidir.
void dfs(int v, int p = -1) {
və -ni ilkinləşdiririk:
sum[v] = 1; dp[v] = 0;
Təpə -nin övladları üzrə DFS icra edirik:
for (int to : g[v]) if (to != p) { dfs(to, v); sum[v] += sum[to]; }
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 } }
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); }
Dərinlikə-öncə axtarışı -ci təpədən başlat.
dfs(1);
Cavabı çap et.
printf("%d\n", dp[1]);