转自
因为生成树中只有一条边属于割集,那么割对生成树来说只是分成了两个子树,那么就考虑割生成树上割哪条边是最优的。
首先对生成树进行建树剖,对于每条非树边的两个端点u和v,对 u –> v 在生成树上的简单路径上的边权值加一,最后找到所有边权值最小的边,就是属于最小割的边。
所有对找到的这条边的权值做贡献的边,一定是跨越了以这条边分开的两个子树,即如果要分开这两个子树,这些边也要割掉,这些边就是要求的最小割集。
这题卡常数,所以路径上边权加一的操作不能用线段树或者树状数组来更新,因为是操作完成后才对边权进行遍历,所以可以用差分前缀和来计算每条边被更新了几次。
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NavigableSet;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;
import java.util.SortedSet;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) throws IOException{
StreamTokenizer cin = new StreamTokenizer(new BufferedInputStream(System.in));
InputReader in = new InputReader(System.in) ;
PrintWriter out = new PrintWriter(System.out) ;
int t = in.nextInt() ;
for(int i = 1 ; i <= t ; i++){
out.print("Case #" + i + ": ");
new Task().solve(in, out) ;
}
out.flush();
}
}
class Task{
public void solve(InputReader in , PrintWriter out) throws IOException{
int j , u , v , k , t , T = 1 , m , d ;
n = in.nextInt() ;
m = in.nextInt() ;
init() ;
for(int i = 1 ; i < n ; i++){
u = in.nextInt() ;
v = in.nextInt() ;
addedge(u , v) ;
}
dfs1(1 , 0 , 0) ;
dfs2(1 , 1) ;
for(int i = 1 ; i <= m - n + 1 ; i++){
u = in.nextInt() ;
v = in.nextInt() ;
if(tid[u] < tid[v]){
u ^= v ; v ^= u ; u ^= v ;
}
change2(u , v , 1) ;
}
gsum2() ;
long res = ans2[2] ;
for(int i = 2 ; i <= n ; i++) res = Math.min(res , ans2[i]) ;
out.println(res+1) ;
}
static final int maxn = 20008 ;
static long[] c1 = new long[maxn] ;
static long[] c2 = new long[maxn] ;
static long[] ans1 = new long[maxn] ;
static long[] ans2 = new long[maxn] ;
int n ;
static int[] siz = new int[maxn] ;
static int[] top = new int[maxn] ;
static int[] son = new int[maxn] ;
static int[] dep = new int[maxn] ;
static int[] tid = new int[maxn] ;
static int[] fa = new int[maxn] ;
static int[] rank = new int[maxn] ;
static int[] head = new int[maxn] ;
static int[] to = new int[200008*2] ;
static int[] next = new int[200008*2] ;
int edge ;
int tim ;
void init(){
Arrays.fill(head, -1) ;
Arrays.fill(son, -1) ;
Arrays.fill(c2, 0) ;
tim = edge = 0 ;
}
void addedge(int u , int v){
to[edge] = v ; next[edge] = head[u] ; head[u] = edge++ ;
to[edge] = u ; next[edge] = head[v] ; head[v] = edge++ ;
}
void dfs1(int u , int father , int d){
dep[u] = d ;
fa[u] = father ;
siz[u] = 1 ;
for(int i = head[u] ; i != -1 ; i = next[i]){
int v = to[i] ;
if(v != father){
dfs1(v , u , d+1) ;
siz[u] += siz[v] ;
if(son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v ;
}
}
}
void dfs2(int u , int tp){
top[u] = tp ;
tid[u] = ++tim ;
rank[tid[u]] = u ;
if(son[u] == -1) return ;
dfs2(son[u] , tp) ;
for(int i = head[u] ; i != -1 ; i = next[i]){
int v = to[i] ;
if(v != son[u] && v != fa[u]) dfs2(v , v) ;
}
}
void in1(int l , int r , int c){
c1[l] += c ;
c1[r+1] -= c ;
}
void gsum1(){
long t = 0 ;
for(int i = 1 ; i <= n ; i++){
t += c1[i] ;
ans1[i] = t ;
}
}
void in2(int l , int r , int c){
c2[l] += c ;
c2[r+1] -= c ;
}
void gsum2(){
long t = 0 ;
for(int i = 1 ; i <= n ; i++){
t += c2[i] ;
ans2[i] = t ;
}
}
void change2(int x , int y , int d){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]){
x ^= y ; y ^= x ; x ^= y ;
}
in2(tid[top[x]] , tid[x] , d) ;
x = fa[top[x]] ;
}
if(x == y) return ;
if(dep[x] > dep[y]){
x ^= y ; y ^= x ; x ^= y ;
}
in2(tid[x]+1 , tid[y] , d) ;
}
void change1(int x , int y , int d){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]){
x ^= y ; y ^= x ; x ^= y ;
}
in1(tid[top[x]] , tid[x] , d) ;
x = fa[top[x]] ;
}
if(dep[x] > dep[y]){
x ^= y ; y ^= x ; x ^= y ;
}
in1(tid[x] , tid[y] , d) ;
}
}
class InputReader{
public BufferedReader reader;
public StringTokenizer tokenizer;
public InputReader(InputStream stream){
reader = new BufferedReader(new InputStreamReader(stream), 32768) ;
tokenizer = null ;
}
public String next(){
while(tokenizer == null || ! tokenizer.hasMoreTokens()){
try{
tokenizer = new StringTokenizer(reader.readLine());
}catch (IOException e) {
throw new RuntimeException(e);
}
}
return tokenizer.nextToken();
}
public int nextInt(){
return Integer.parseInt(next());
}
public long nextLong(){
return Long.parseLong(next());
}
public double nextDouble(){
return Double.parseDouble(next());
}
}