题目链接:http://codeforces.com/problemset/problem/240/E
题意:一个五万个点和五万条边的图,每条边的边权为1或0,问以1为根的最小树形图,并输出方案。
解法:传统最小树形图的复杂度为VE,但此图边权比较特殊,最小树形图的定义为除根节点外每个点都有入边,根节点有出边,且无环。因此可以首先使用边权为0的边为每个点找一条入边,对于仍然没有入度的点再使用边权为1的边,但只要无法处理0边形成的环,因此要首先对0边的树进行强连通缩点,对于缩点后的dag先用0边为每个点找入边,然后利用类似于prim的思想:将当前根节点能够延伸到的点放到队列中(开始时队列中的点为根节点),对于取出每个点的邻接边,如果临界点未被访问则加入队列,如果临界点未被访问且边权为1则此边是最终答案中的边。
import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.util.Arrays; import java.util.Scanner; public class e { int maxn = 100010; class node { int id, be, ne, val; node(int i, int b, int e, int v) { id = i; be = b; ne = e; val = v; } } class SCC { int E[] = new int[maxn], len; node buf[] = new node[maxn]; int dfn[] = new int[maxn], low[] = new int[maxn], cnt; int stack[] = new int[maxn], top; boolean instack[] = new boolean[maxn]; int id[] = new int[maxn], n, idx; void init(int n) { this.n = n; Arrays.fill(E, -1); len = 0; } void add(int i, int a, int b, int v) { buf[len] = new node(i, b, E[a], v); E[a] = len++; } void dfs(int a) { dfn[a] = low[a] = ++cnt; instack[a] = true; stack[top++] = a; int b = -1; for (int i = E[a]; i != -1; i = buf[i].ne) { b = buf[i].be; if (buf[i].val != 0) continue; if (dfn[b] == 0) { dfs(b); if (low[b] < low[a]) low[a] = low[b]; } else if (instack[b]) low[a] = Math.min(low[a], dfn[b]); } if (low[a] == dfn[a]) { idx++; do { b = stack[--top]; instack[b] = false; id[b] = idx; } while (b != a); } } void solve() { cnt = idx = top = 0; Arrays.fill(dfn, 0); Arrays.fill(low, 0); Arrays.fill(instack, false); for (int i = 1; i <= n; i++) if (dfn[i] == 0) dfs(i); } void shrink() { dp.init(idx); dp.rt = id[1]; for (int a = 1; a <= n; a++) for (int i = E[a]; i != -1; i = buf[i].ne) { int b = buf[i].be; if (id[a] != id[b]) dp.add(buf[i].id, id[a], id[b], buf[i].val); } } } class DP { int E[] = new int[maxn], len, n, rt; node buf[] = new node[maxn]; void add(int i, int a, int b, int v) { buf[len] = new node(i, b, E[a], v); E[a] = len++; } void init(int n) { this.n = n; Arrays.fill(E, -1); len = 0; } boolean vis[] = new boolean[maxn]; void dfs(int a) { if (vis[a]) return; vis[a] = true; for (int i = E[a]; i != -1; i = buf[i].ne) dfs(buf[i].be); } void solve() { dfs(rt); for (int i = 1; i <= n; i++) if (!vis[i]) { ps.println(-1); return; } Arrays.fill(vis, false); for (int i = 0; i < len; i++) if (buf[i].val == 0) vis[buf[i].be] = true; int ans = 0; vis[rt] = true; for (int i = 1; i <= n; i++) if (!vis[i]) ans++; ps.println(ans); if (ans == 0) return; int stack[] = new int[n + 10], top = 0; boolean instack[] = new boolean[n + 10]; stack[++top] = rt; instack[rt] = true; while (top != 0) { int a = stack[top--]; for (int i = E[a]; i != -1; i = buf[i].ne) { int b = buf[i].be; if (!vis[b]){ vis[b] =instack[b]= true; stack[++top] = b; ps.print(buf[i].id+" "); } else if (!instack[b]&&buf[i].val==0) { stack[++top] = b; instack[b] = true; } } } ps.println(); } } DP dp = new DP(); SCC scc = new SCC(); PrintStream ps; Scanner scan; int n, m; void run() throws FileNotFoundException { scan = new Scanner(new File("input.txt")); ps = new PrintStream(new File("output.txt")); n = scan.nextInt(); m = scan.nextInt(); scc.init(n); for (int i = 1; i <= m; i++) scc.add(i, scan.nextInt(), scan.nextInt(), scan.nextInt()); scc.solve(); scc.shrink(); dp.solve(); } public static void main(String[] args) throws IOException { new e().run(); } }