Fuga del Boss#9888

Fuga del Boss (Editoriale)

I rifugi e i passaggi formano un albero di vertici. Bloccare un passaggio equivale a rimuovere un arco. È richiesto aggiungere il minor numero possibile di archi in modo che, dopo la rimozione di un qualsiasi arco, il grafo rimanga connesso, cioè che non contenga ponti. Se il grafo rimane connesso, allora da qualsiasi rifugio si può raggiungere il quartier generale, quindi il numero del quartier generale non influisce sulla risposta.

Stima inferiore. Chiamiamo foglia un vertice di grado , e sia il numero di foglie nell'albero. L'unico arco che esce da una foglia è un ponte. Affinché smetta di essere un ponte, è necessario tracciare verso la foglia almeno un nuovo arco. Ogni nuovo arco ha due estremità, quindi copre al massimo due foglie. Dunque saranno necessari almeno nuovi archi.

Costruzione. Dimostriamo che archi sono sempre sufficienti. Avviamo una ricerca in profondità da un vertice qualsiasi ed elenchiamo le foglie nell'ordine in cui vengono visitate: l_0, l_1, ..., l_{L-1}. Indichiamo k = \lfloor L / 2 \rfloor e colleghiamo le foglie i cui numeri in questo elenco differiscono di :

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

Si ottengono esattamente archi, e ogni foglia risulta essere estremità di almeno uno di essi. Se è dispari, la foglia l_k partecipa a due archi.

Esempio. Nella figura L = 5 e k = 2. Le foglie sono colorate, e accanto a ciascuna è indicato il suo numero nell'ordine di visita. Gli archi aggiunti (3, 5), (4, 7) e sono mostrati tratteggiati, la foglia partecipa a due di essi.

Perché non restano ponti. Appendiamo l'albero per il vertice da cui è partita la ricerca. L'arco dell'albero (, genitore di ) smette di essere un ponte se almeno un nuovo arco collega una foglia all'interno del sottoalbero di con una foglia al di fuori di esso. La ricerca in profondità visita il sottoalbero di interamente, senza interruzioni, quindi le foglie di questo sottoalbero compaiono nell'elenco consecutivamente e occupano un segmento di lunghezza . Nel sottoalbero c'è almeno una foglia, ma tutte le foglie non possono trovarsi al suo interno: o la radice ha altri sottoalberi con proprie foglie, oppure la radice stessa è una foglia. Perciò 1 \le s \le L - 1.

Il partner di ogni foglia nel nuovo arco si trova a esattamente posizioni di distanza da essa nell'elenco. Consideriamo due casi:

In entrambi i casi si troverà un nuovo arco che collega il sottoalbero di v con il resto dell'albero. Dunque, nessun arco rimarrà un ponte.

Teorema. Il numero minimo di nuovi archi è pari a . È sufficiente elencare le foglie nell'ordine della ricerca in profondità e collegare ogni foglia l_i con la foglia l_i + \lfloor L / 2 \rfloor per i = 0, 1, ..., \lceil L / 2 \rceil - 1.

Per n = 2 entrambe le estremità dell'unico arco sono foglie, e la risposta sarà un secondo arco tra di esse.

Esempio

Nel primo esempio, la ricerca a partire dal vertice visita le foglie nell'ordine . Qui L = 3, k = 1, e il programma aggiunge gli archi e :

La risposta differisce da quella fornita nel testo del problema, ma è comunque corretta: va bene qualsiasi insieme corretto di due archi.

Nel secondo esempio, le foglie nell'ordine di visita sono: 4, 5, 2, 3. Qui L = 4, k = 2:

0

1

2

3

l_i

4

5

2

3

Colleghiamo l_0 - l_2 e l_1 - l_3, cioè aggiungiamo gli archi e :

Verifichiamo, ad esempio, l'arco . Le foglie del sottoalbero del vertice sono e , che occupano nell'elenco il segmento delle posizioni e . L'arco esce da questo sottoalbero, quindi l'arco non è un ponte.

Implementazione dell'algoritmo

Memorizziamo l'albero come lista di adiacenza . Le foglie nell'ordine della visita in profondità vengono raccolte nell'array .

vector<vector<int>> g;
vector<int> leaves;

La funzione dfs esegue la ricerca in profondità a partire dal vertice . Il parametro è il genitore del vertice , per la radice p = -1. Per prima cosa visitiamo tutti i figli del vertice .

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

Un vertice di grado è una foglia, lo inseriamo nell'array . Le foglie di qualsiasi sottoalbero finiscono nell'array consecutivamente.

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

Parte principale del programma. Leggiamo i dati in input. Il numero del quartier generale non influisce sulla risposta.

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

Avviamo la ricerca in profondità dal vertice e raccogliamo le foglie. La variabile contiene il numero di foglie, e r = \lceil m / 2 \rceil è il numero di nuovi passaggi.

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

Stampiamo la risposta: la foglia con numero nell'elenco viene collegata alla foglia con numero i + \lfloor m / 2 \rfloor.

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