Suç Baronunun Kaçışı#9888

Suç Baronunun Kaçışı (Editoryal)

Sığınaklar ve geçitler köşeli bir ağaç oluşturur. Bir geçidin kapatılması, bir kenarın silinmesi anlamına gelir. Herhangi bir kenar silindiğinde grafın bağlantılı kalması için, yani grafta köprü kalmaması için eklenmesi gereken en az kenar sayısı bulunmalıdır. Graf bağlantılı kalırsa, herhangi bir sığınaktan karargaha ulaşmak mümkündür, bu yüzden karargahın numarası cevabı etkilemez.

Alt sınır. Derecesi olan köşeye yaprak diyelim ve ağaçta tane yaprak olsun. Bir yaprak köşeden çıkan tek kenar bir köprüdür. Bu kenarın köprü olmaktan çıkması için yaprağa en az bir yeni kenar çekilmesi gerekir. Her yeni kenarın iki ucu vardır, bu yüzden en fazla iki yaprağı kapsar. Demek ki en az yeni kenar gerekecektir.

İnşa. kenarın her zaman yeterli olduğunu gösterelim. Herhangi bir köşeden derinlik öncelikli arama başlatalım ve yaprakları dolaşım sırasına göre yazalım: l_0, l_1, ..., l_{L-1}. k = \lfloor L / 2 \rfloor olarak gösterelim ve bu listede numaraları kadar farklı olan yaprakları birleştirelim:

l_0 - l_k, l_1 - l_{k+1}, ..., l_{\lceil L / 2 \rceil - 1} - l_{\lceil L / 2 \rceil - 1 + k}

Tam olarak kenar elde edilir ve her yaprak bunlardan en az birinin ucu olur. tek olduğunda l_k yaprağı iki kenara dahil olur.

Örnek. Şekilde L = 5 ve k = 2. Yapraklar boyanmıştır, her birinin yanında dolaşım sırasındaki numarası belirtilmiştir. Eklenen (3, 5), (4, 7) ve kenarları kesikli çizgiyle gösterilmiştir, yaprak bunlardan ikisine dahildir.

Neden köprü kalmıyor. Ağacı aramanın başlatıldığı köşeden asalım. Ağacın (, 'nin ebeveyni) kenarı, eğer en az bir yeni kenar alt ağacının içindeki bir yaprağı dışındaki bir yaprakla birleştiriyorsa köprü olmaktan çıkar. Derinlik öncelikli arama, alt ağacını kesintisiz olarak tamamen dolaşır, bu yüzden bu alt ağacın yaprakları listede art arda gelir ve uzunluğunda bir aralık kaplar. Alt ağaçta en az bir yaprak vardır ve yaprağın tümü onda bulunamaz: ya kökün kendi yapraklarına sahip başka alt ağaçları vardır, ya da kökün kendisi bir yapraktır. Bu yüzden 1 \le s \le L - 1.

Yeni kenardaki her yaprağın ortağı, listede ondan tam olarak pozisyon uzaktadır. İki durumu inceleyelim:

Her iki durumda da v alt ağacını ağacın geri kalanıyla birleştiren yeni bir kenar bulunacaktır. Demek ki hiçbir kenar köprü olarak kalmaz.

Teorem. En az yeni kenar sayısı 'ye eşittir. Yaprakları derinlik öncelikli arama sırasına göre yazmak ve her l_i yaprağını i = 0, 1, ..., \lceil L / 2 \rceil - 1 için l_i + \lfloor L / 2 \rfloor yaprağıyla birleştirmek yeterlidir.

n = 2 olduğunda, tek kenarın her iki ucu da yapraktır ve cevap bu ikisi arasındaki ikinci kenar olacaktır.

Örnek

İlk örnekte, köşesinden başlayan arama yaprakları sırasıyla dolaşır. Burada L = 3, k = 1 ve program ve kenarlarını ekler:

Cevap, koşulda verilenden farklıdır, ancak o da doğrudur: herhangi bir doğru iki kenarlık küme uygundur.

İkinci örnekte, dolaşım sırasındaki yapraklar: 4, 5, 2, 3. Burada L = 4, k = 2:

0 1 2 3

l_i 4 5 2 3

l_0 - l_2 ve l_1 - l_3'ü birleştiriyoruz, yani ve kenarlarını ekliyoruz:

Örneğin, kenarını kontrol edelim. köşesinin alt ağacının yaprakları ve 'tir, bunlar listede ve pozisyonlarından oluşan bir aralık kaplar. kenarı bu alt ağaçtan çıkar, bu yüzden kenarı köprü değildir.

Algoritmanın uygulanması

Ağacı komşuluk listesi şeklinde saklıyoruz. Derinlik öncelikli dolaşım sırasındaki yaprakları dizisinde topluyoruz.

vector	extless vector	extless int	extgreater 	extgreater g;
vector	extless int	extgreater leaves;

dfs fonksiyonu köşesinden derinlik öncelikli arama yapar. parametresi köşesinin ebeveynidir, kök için p = -1. Önce köşesinin tüm çocuklarını dolaşırız.

void dfs(int v, int p = -1)
{
  for (int to : g[v])
    if (to != p) dfs(to, v);

Derecesi olan köşe bir yapraktır, onu dizisine ekleriz. Herhangi bir alt ağacın yaprakları diziye art arda düşer.

  if (g[v].size() == 1)
    leaves.push_back(v);
}

Programın ana bölümü. Girdi verilerini okuyoruz. Karargahın numarası cevabı etkilemez.

  scanf("%d %d", 		&n, &h);
  g.resize(n);
  for (i = 1; i 	extless n; i++)
  {
    scanf("%d %d", 		&a, &b);
    g[a].push_back(b);
    g[b].push_back(a);
  }

köşesinden derinlik öncelikli arama başlatıyoruz ve yaprakları topluyoruz. değişkeni yaprak sayısını, r = \lceil m / 2 \rceil ise yeni geçit sayısını içerir.

  dfs(0);
  m = leaves.size();
  r = (m + 1) / 2;

Cevabı yazdırıyoruz: listede numaralı yaprağı i + \lfloor m / 2 \rfloor numaralı yaprakla birleştiriyoruz.

  printf("%d\n", r);
  for (i = 0; i 	extless r; i++)
    printf("%d %d\n", leaves[i], leaves[i + m / 2]);