Dua Sejoli
Soal#
Informasi#
- Sumber: CompFest 9 - Penyisihan CPC Senior
- Tanggal: 2017-08-12
Arsip#
Ringkasan#
Terdapat $N$ tempat pertemuan dan $N$ jalan dua arah. Terdapat setidaknya satu rute di antara dua tempat pertemuan. Berapa minimum jalan yang harus ditutup untuk memisahkan pasangan dengan laki-laki berada di tempat pertemuan ke-$X_j$ dan perempuan berada di tempat pertemuan ke-$Y_j$, serta banyaknya cara untuk melakukan hal tersebut (modulo $10^9 + 7$)?
Solusi#
Petunjuk#
Pembahasan#
Mari kita modelkan hubungan tempat pertemuan dan jalan sebagai suatu graf. Tempat pertemuan dan jalan secara berurutan direpresentasikan sebagai simpul dan sisi pada graf. Graf ini merupakan keluarga dari pseudotree (pseudoforest dengan satu komponen terhubung). Graf pada soal ini memiliki tepat satu siklus, yang bisa disebut sebagai 1-tree atau unicyclic graph. Kita bisa melakukan partisi pada graf menjadi satu siklus dan beberapa pohon sedemikian sehingga tiap pohon memiliki simpul pada siklus. Setiap pohon akan memiliki simpul akar (root) yang terhubung satu sama lain sehingga membentuk satu siklus.
Terdapat beberapa kasus penempatan simpul $X_j$ dan $Y_j$ pada graf.
- Simpul $X_j$ dan $Y_j$ berada pada pohon yang sama.
Pada kasus ini, kita dapat menutup 1 jalan pada lintasan (path) di antara simpul $X_j$ dan simpul $Y_j$ untuk menghasilkan penutupan jalan paling minimum. Banyaknya cara untuk menutup jalan adalah panjang (banyaknya jalan) dari lintasan tersebut. - Simpul $X_j$ dan $Y_j$ berada pada pohon yang berbeda, namun kedua berada pada simpul akar.
Terdapat dua lintasan berbeda di antara kedua simpul. Kita harus menutup satu jalan pada masing-masing lintasan tersebut. Oleh karena itu, kita dapat menutup 2 jalan untuk menghasilkan penutupan jalan paling minimum. Banyaknya cara untuk menutup jalan adalah panjang lintasan pertama dikali dengan panjang lintasan kedua. - Simpul $X_j$ dan $Y_j$ berada pada pohon yang berbeda dan salah satu atau keduanya tidak berada pada simpul akar.
Pada kasus ini, kita dapat menutup 1 jalan untuk menghasilkan penutupan jalan paling minimum. Jalan yang ditutup adalah jalan yang tidak termasuk ke dalam siklus. Banyaknya cara untuk menutup jalan adalah kedalaman (depth, panjang lintasan dari suatu simpul pada pohon ke simpul akar) dari $X_j$ ditambah kedalaman dari $Y_j$.
Mula-mula, kita akan mencari siklus dan menyimpannya ke dalam sebuah array. Simpul yang saling bertetangga pada siklus juga akan bersebelahan pada array. Elemen pertama dan terakhir pada array juga dianggap bersebelahan.
Misal $\text{index}(v)$ menyatakan indeks simpul $v$ pada array. Panjang salah satu lintasan di antara simpul $X_j$ dan $Y_j$ pada siklus adalah $|\text{index}(X_j) - \text{index}(Y_j)|$. Sedangkan panjang lintasan lainnya adalah $\text{panjang siklus} - |\text{index}(X_j) - \text{index}(Y_j)|$.
Kita juga perlu sebuah prosedur yang efisien untuk menemukan panjang lintasan di antara dua simpul pada suatu pohon. Kita akan menggunakan informasi kedalaman dan fungsi yang menghasilkan LCA (lowest common ancestor) dari dua simpul. LCA dari dua buah simpul $u$ dan $v$ didefinisikan sebagai sebuah simpul $w$ di mana
- kita bisa mencapai $u$ dan $v$ dengan menelusuri nol atau lebih jalan di bawah simpul $w$ dan
- kedalaman dari simpul $w$ adalah yang terbesar dari semua simpul yang memenuhi kondisi sebelumnya.
Misal $\text{depth}(v)$ menyatakan kedalaman simpul $v$ dan $\text{lca}(u, v)$ adalah LCA dari simpul $u$ dan $v$. Panjang lintasan di antara simpul $X_j$ dan $Y_j$ adalah $\text{depth}(X_j) + \text{depth}(Y_j) - 2 \cdot \text{depth}(\text{lca}(X_j, Y_j))$.
Fungsi $\text{index}$, $\text{depth}$, dan $\text{lca}$ dapat diprekomputasi terlebih dahulu ke dalam array. Untuk melakukan prekomputasi fungsi $\text{depth}$ pada suatu pohon, kita bisa menjelajahi graf dimulai dari simpul akar pada pohon tersebut. Untuk melakukan prekomputasi fungsi $\text{lca}$, kita bisa menggunakan struktur data dengan kombinasi algoritma seperti Euler's tour + struktur data yang mendukung range minimum query secara efisien atau sparse table + binary lifting. Solusi saya menggunakan sparse table dan binary lifting untuk menghitung fungsi $lca$ secara efisien.
Kode#
using namespace std;
typedef pair <int, int> pii;
const int MAXN = 50000;
const int MOD = 1E9 + 7;
int T, N, Q, LOG_N;
int cycle_start, cycle_size;
int parent;
int cycle_idx;
int depth;
int sparta;
vector <pii> al;
vector <int> cycle;
bool vis;
bool push_cycle;
void
void
void
int
int
int