Bài toán tìm cây khung nhỏ nhất trong đồ thị

/*input
4 5
1 2 101
1 3 100
2 3 2
2 4 2
3 4 1
*/

#include "bits/stdc++.h"

using

namespace

std

;

#define fi first
#define se second

const

int

N

=

1e5

+

5

;

enum

EdgeType

{

NONE

,

// không cây nào chứa

ANY

,

// tất cả các cây đều chứa

ATL

// ít nhất 1 cây chứa

};

struct

Edge

{

int

u

,

v

,

c

,

id

;

Edge

(

int

_u

,

int

_v

,

int

_c

,

int

_id

)

:

u

(

_u

),

v

(

_v

),

c

(

_c

),

id

(

_id

)

{};

};

struct

Dsu

{

vector

<

int

>

par

;

void

init

(

int

n

)

{

par

.

resize

(

n

+

5

,

0

);

for

(

int

i

=

1

;

i

<=

n

;

i

++

)

par

[

i

]

=

i

;

}

int

find

(

int

u

)

{

if

(

par

[

u

]

==

u

)

return

u

;

return

par

[

u

]

=

find

(

par

[

u

]);

}

bool

join

(

int

u

,

int

v

)

{

u

=

find

(

u

);

v

=

find

(

v

);

if

(

u

==

v

)

return

false

;

par

[

v

]

=

u

;

return

true

;

}

}

dsu

;

vector

<

pair

<

int

,

int

>>

g

[

N

];

int

low

[

N

],

num

[

N

],

Time

=

0

;

int

n

,

m

;

EdgeType

res

[

N

];

vector

<

Edge

>

edges

;

void

dfs

(

int

u

,

int

idx

)

{

num

[

u

]

=

low

[

u

]

=

++

Time

;

for

(

auto

&

e

:

g

[

u

])

{

int

v

=

e

.

fi

;

int

id

=

e

.

se

;

if

(

id

==

idx

)

continue

;

if

(

num

[

v

]

==

0

)

{

dfs

(

v

,

id

);

low

[

u

]

=

min

(

low

[

u

],

low

[

v

]);

if

(

low

[

v

]

==

num

[

v

])

{

// nếu cạnh là cầu thì mọi cây đều phải chứa

res

[

id

]

=

EdgeType

::

ANY

;

}

}

else

{

low

[

u

]

=

min

(

low

[

u

],

num

[

v

]);

}

}

}

void

solve

(

vector

<

Edge

>

&

pen

)

{

// xử lý các nhóm cạnh có cùng trọng số

if

(

pen

.

empty

())

return

;

// khởi tạo đồ thị nối các thành phần liên thông

for

(

int

i

=

0

;

i

<

pen

.

size

();

i

++

)

{

// sử dụng đỉnh cha trong dsu làm đỉnh đại diện cho thành phần liên thông

pen

[

i

].

u

=

dsu

.

find

(

pen

[

i

].

u

);

pen

[

i

].

v

=

dsu

.

find

(

pen

[

i

].

v

);

g

[

pen

[

i

].

u

].

clear

();

g

[

pen

[

i

].

v

].

clear

();

num

[

pen

[

i

].

u

]

=

num

[

pen

[

i

].

v

]

=

0

;

}

for

(

auto

e

:

pen

)

{

if

(

e

.

u

==

e

.

v

)

{

// nếu 2 đỉnh cùng thuộc 1 thành phần liên thông

res

[

e

.

id

]

=

EdgeType

::

NONE

;

}

else

{

// nếu 2 đỉnh nối 2 thành phần liên thông khác nhau lại với nhau

res

[

e

.

id

]

=

EdgeType

::

ATL

;

// thêm cạnh vào đồ thị

g

[

e

.

v

].

push_back

({

e

.

u

,

e

.

id

});

g

[

e

.

u

].

push_back

({

e

.

v

,

e

.

id

});

}

}

// tìm cạnh cầu

for

(

auto

&

e

:

pen

)

if

(

num

[

e

.

u

]

==

0

)

dfs

(

e

.

u

,

-

1

);

// sau khi hoàn thành, ta thực hiện hợp các cạnh vào cây khung

for

(

auto

&

e

:

pen

)

dsu

.

join

(

e

.

u

,

e

.

v

);

}

int

main

()

{

ios_base

::

sync_with_stdio

(

0

);

cin

.

tie

(

0

);

cout

.

tie

(

0

);

cin

>>

n

>>

m

;

for

(

int

i

=

1

;

i

<=

m

;

i

++

)

{

int

u

,

v

,

c

;

cin

>>

u

>>

v

>>

c

;

edges

.

push_back

({

u

,

v

,

c

,

i

});

}

dsu

.

init

(

n

);

sort

(

edges

.

begin

(),

edges

.

end

(),

[](

Edge

x

,

Edge

y

)

{

return

x

.

c

<

y

.

c

;

});

vector

<

Edge

>

pen

;

for

(

auto

&

e

:

edges

)

{

if

(

!

pen

.

empty

()

&&

pen

.

back

().

c

==

e

.

c

)

{

pen

.

push_back

(

e

);

}

else

{

solve

(

pen

);

pen

=

{

e

};

}

}

solve

(

pen

);

// in ra kết quả

for

(

int

i

=

1

;

i

<=

m

;

i

++

)

{

if

(

res

[

i

]

==

EdgeType

::

NONE

)

cout

<<

"none

\n

"

;

else

if

(

res

[

i

]

==

EdgeType

::

ANY

)

cout

<<

"any

\n

"

;

else

cout

<<

"at least one

\n

"

;

}

}