Ağacda məsafə#4104

Ağacda məsafə (Həll təhlili)

Tutaq ki, dp[v][x] — köklü alt ağacda vərəsələr arasından -dan uzaqlıqda yerləşən zirvələrin sayıdır. dp[v][0] = 1 qəbul edək, çünki zirvəsindən 0 uzaqlıqda yalnız özü yerləşir.

Tutaq ki, zirvəsinin övladları u_1, u_2, ..., u_p var. Onda köklü alt ağacda zirvəsindən uzaqlıqda yerləşən zirvələrin sayı, hər bir u_1, u_2, ..., u_p zirvəsindən x-1 uzaqlıqda yerləşən zirvələrin sayı cəminə bərabərdir.

dp[v][x] = \sum_{i = 1}^p dp[u_i][x-1]

Tutaq ki, kökü zirvəsi olan alt ağacı nəzərdən keçiririk. Bu alt ağacda tamamilə yerləşən və zirvəsindən keçən uzunluğu olan yolların sayını hesablayaq. Bu cür yolları iki qrupa bölək:

Köklü alt ağacdan keçən uzunluğu olan bir yol nəzərdən keçirək. Yolun bir ucundan zirvəsinə qədər olan məsafə x~(1 \le x \le k - 1) olsun. Tutaq ki, həmin ucu u_1 zirvəsinin alt ağacında yerləşir. Onda yolun ikinci ucundan zirvəsinə qədər olan məsafə k - x-ə bərabərdir və bu ikinci ucu u_1 zirvəsinin kökü olan alt ağacdan başqa istənilən alt ağacda yerləşə bilər.

Əgər yolun ilk ucundan zirvəsinə qədər olan məsafə -ə bərabərdirsə, onda həmin ucdan u_1 zirvəsinə qədər olan məsafə x - 1-ə bərabərdir. Belə bir ilk uc seçməyin sayı dp[u_1][x - 1]-ə bərabərdir.

İkinci ucu seçməyin sayı zirvəsindən k - x uzaqlıqda yerləşən, lakin u_1 köklü alt ağaca aid olmayan zirvələrin sayına bərabərdir. Bu isə aşağıdakına bərabərdir:

dp[v][k - x] - dp[u_1][k - x - 1]

Vurulanın qaydalarına əsasən, bu cür yolların ümumi sayı

dp[u_1][x - 1] \cdot (dp[v][k - x] - dp[u_1][k - x - 1])

Beləliklə, bu cür yolların ümumi sayı

dp[v][k] + 1/2 \sum_{i = 1}^p \sum_{x = 1}^{k - 1} dp[u_i][x-1] \cdot (dp[v][k-x]-dp[u_i][k-x-1])

Yuxarıdakı ifadəni ağacın bütün zirvələri üçün toplamaq lazımdır.

Məsələnin nümunəsi

Misala verilmiş ağacın quruluşuna nəzər salaq.

Alqoritmin realizasiyası

Massiv ağacı qıraq siyahıları ilə təqdim edir. Massiv dinamik proqramlaşdırmanın tətbiqi üçün istifadə olunur.

vector<vector<int> > g;
vector<vector<int> > dp;

dfs funksiyası zirvəsindən başlayaraq dərinlik birinci axtarışı yerinə yetirir. Burada zirvəsinin qohumunu göstərir. Axtarış zamanı massivini hazırlayırıq.

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

Massiv -i indeksləyirik.

  dp[v].resize(k+1);
  dp[v][0] = 1;

Bütün zirvələri keçirik ki, zirvəsindən ona keçid mövcud olsun.

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

    dfs(to,v);

    for(int x = 1; x <= k; x++)
      dp[v][x] += dp[to][x-1];
  }
}

dfsRes funksiyası zirvəsindən başlayaraq dərinlik birinci axtarışı yerinə yetirir və məcruci cavabı hesablamaq üçün nəzərdə tutulur.

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

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

    dfsRes(to,v);

    for(int x = 1; x < k; x++)
      resA += 1LL * dp[to][x-1] * (dp[v][k-x] - dp[to][k-x-1]);
  }

  resB += dp[v][k];
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq. Ağacı qururuq.

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);
}

Birinci dərinlik axtarışı ilə massivini qururuq.

dp.resize(n+1);
dfs(1);

Axtarılan cavabı hesablamaq və çıxış etmək.

resA = resB = 0;
dfsRes(1);
res = resA / 2 + resB;
printf("%lld\n",res);

Python icrası

dfs funksiyası zirvəsindən başlayaraq dərinlik birinci axtarışı yerinə yetirir. Burada zirvəsinin qohumunu göstərir. Axtarış zamanı massivini hazırlayırıq.

def dfs(v, p):

Massiv -i indeksləyirik.

  dp[v] = [0] * (k + 1)
  dp[v][0] = 1

Bütün zirvələri keçirik ki, zirvəsindən ona keçid mövcud olsun.

  for to in g[v]:
    if to == p: continue
    dfs(to, v)

    for x in range(1, k + 1):
      dp[v][x] += dp[to][x - 1]

dfsRes funksiyası zirvəsindən başlayaraq dərinlik birinci axtarışı yerinə yetirir və məcruci cavabı hesablamaq üçün nəzərdə tutulur.

def dfsRes(v, p):
  global resA, resB
  for to in g[v]:
    if to == p: continue
    dfsRes(to, v)

    for x in range(1, k):
      resA += dp[to][x - 1] * (dp[v][k - x] - dp[to][k - x - 1])

  resB += dp[v][k]

Proqramın əsas hissəsi. Ağacın zirvələri və arasındakı məsafəni oxuyuruq.

n, k = map(int, input().split())

siyahısında ağacın qraf siyahıları ilə təqdimatı saxlanılır. siyahısı dinamik proqramlaşdırmanın tətbiqi üçün istifadə olunur.

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

Kənarların siyahısını oxuyuruq. Ağacı qururuq.

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

Birinci dərinlik axtarışı ilə massivini qururuq.

dfs(1, -1)

Axtarılan cavabı hesablamaq və çıxış etmək.

resA, resB = 0, 0
dfsRes(1, -1)
res = resA // 2 + resB
print(res)