求满足如下条件的边的个数:
增加该边的容量,可以使得最大流变大.
step1:求一遍最大流,如果某条边符合我们的要求,那么这条边必然是满流的,否则增加容量也没有用;
step2:对于漫流边(u->v),那么必然存在从源点到u的不饱和的路径,同样从v到汇点的不饱和的路径;
public class Main {
public static void main(String[] args) {
new Task().solve();
}
}
@SuppressWarnings("unchecked")
class Task {
InputReader in = new InputReader(System.in);
PrintWriter out = new PrintWriter(System.out);
List<Integer>[] adj , reverseAdj ;
List<DinicGraph.Edge> fullEdge ;
void dfs(int u , List<Integer>[] grid , boolean[] color){
color[u] = true ;
for(int v : grid[u]){
if(! color[v]) dfs(v , grid , color) ;
}
}
void solve() {
int n = in.nextInt() ;
int m = in.nextInt() ;
DinicGraph graph = new DinicGraph(n) ;
while(m-- > 0){
int u = in.nextInt() ;
int v = in.nextInt() ;
long w = in.nextLong() ;
if(w != 0)
graph.addEdge(u , v , w) ;
}
adj = new List[n] ;
reverseAdj = new List[n] ;
for(int i = 0 ; i < n ; i++){
adj[i] = new ArrayList<Integer>() ;
reverseAdj[i] = new ArrayList<Integer>() ;
}
fullEdge = new ArrayList<DinicGraph.Edge>() ;
graph.getMaxFlow(0 , n-1) ;
for(int u = 0 ; u < n ; u++){
for(DinicGraph.Edge edge : graph.adj[u]){
if(! edge.isDir) continue ;
if(edge.flow == edge.capacity)
fullEdge.add(edge) ;
else{
adj[u].add(edge.to) ;
reverseAdj[edge.to].add(u) ;
}
}
}
boolean[] canFromSource = new boolean[n] ;
Arrays.fill(canFromSource , false) ;
boolean[] canFromTarget = new boolean[n] ;
Arrays.fill(canFromTarget , false) ;
dfs(0, adj , canFromSource) ;
dfs(n-1, reverseAdj , canFromTarget) ;
int sum = 0 ;
for(DinicGraph.Edge edge : fullEdge){
if(canFromSource[edge.from] && canFromTarget[edge.to])
sum++ ;
}
out.println(sum) ;
out.flush();
}
}
@SuppressWarnings("unchecked")
class DinicGraph {
class Edge {
int from, to;
long flow, capacity;
Edge reverse;
boolean isDir ;
Edge(int from, int to, long flow, long capacity , boolean isDir) {
this.from = from;
this.to = to;
this.flow = flow;
this.capacity = capacity;
this.isDir = isDir ;
}
}
List<Edge>[] adj;
int N, distance[];
DinicGraph(int N) {
this.N = N;
adj = new List[N];
for (int i = 0; i < adj.length; i++) {
adj[i] = new ArrayList<Edge>();
}
distance = new int[N];
}
Edge addEdge(int from, int to, long capacity) {
Edge e1 = new Edge(from, to, 0, capacity , true) ;
Edge e2 = new Edge(to, from, 0, 0 , false) ;
e1.reverse = e2;
e2.reverse = e1;
adj[from].add(e1);
adj[to].add(e2);
return e1;
}
boolean bfs(int source, int target) {
Arrays.fill(distance, Integer.MAX_VALUE);
distance[source] = 0;
Queue<Integer> Q = new LinkedList<Integer>();
Q.add(source);
while (!Q.isEmpty()) {
int u = Q.poll() ;
for (Edge e : adj[u]) {
int v = e.to;
if (e.capacity - e.flow > 0 && distance[v] == Integer.MAX_VALUE) {
distance[e.to] = distance[u] + 1;
Q.add(v);
if (v == target) {
return true;
}
}
}
}
return false;
}
public long dfs(int u, int target, long cMin) {
if (u == target) {
return cMin ;
}
for (Edge e : adj[u]) {
int v = e.to;
if (distance[v] != distance[u] + 1 || e.capacity - e.flow == 0) {
continue;
}
long add = dfs(v, target, Math.min(cMin, e.capacity - e.flow));
if (add == 0) {
continue;
}
e.flow += add;
e.reverse.flow -= add;
return add;
}
return 0L ;
}
public long getMaxFlow(int source, int target) {
long flow = 0;
while (bfs(source, target)) {
while (true) {
long add = dfs(source, target, Long.MAX_VALUE);
if (add == 0) {
break;
}
flow += add;
}
}
return flow;
}
}
class InputReader {
public BufferedReader reader;
public StringTokenizer tokenizer;
public InputReader(InputStream stream) {
reader = new BufferedReader(new InputStreamReader(stream), 32768);
tokenizer = new StringTokenizer("");
}
private void eat(String s) {
tokenizer = new StringTokenizer(s);
}
public String nextLine() {
try {
return reader.readLine();
} catch (Exception e) {
return null;
}
}
public boolean hasNext() {
while (!tokenizer.hasMoreTokens()) {
String s = nextLine();
if (s == null)
return false;
eat(s);
}
return true;
}
public String next() {
hasNext();
return tokenizer.nextToken();
}
public int nextInt() {
return Integer.parseInt(next());
}
public long nextLong() {
return Long.parseLong(next());
}
public double nextDouble() {
return Double.parseDouble(next());
}
public BigInteger nextBigInteger() {
return new BigInteger(next());
}
}