// Returns topological order or empty array if cycle exists
function topoSortKahn(adj) {
const n = adj.length;
const indeg = Array(n).fill(0);
for (let u = 0; u < n; u++) for (const v of adj[u]) indeg[v]++;
const q = [];
for (let u = 0; u < n; u++) if (indeg[u] === 0) q.push(u);
const order = [];
for (let qi = 0; qi < q.length; qi++) {
const u = q[qi]; order.push(u);
for (const v of adj[u]) if (--indeg[v] === 0) q.push(v);
}
return order.length === n ? order : [];
}
// Topo order by DFS postorder
function topoSortDFS(adj) {
const n = adj.length, vis = Array(n).fill(0); // 0=unseen,1=stack,2=done
const order = []; let hasCycle = false;
function go(u) {
vis[u] = 1;
for (const v of adj[u]) {
if (vis[v] === 0) go(v);
else if (vis[v] === 1) { hasCycle = true; }
}
vis[u] = 2; order.push(u);
}
for (let u = 0; u < n; u++) if (vis[u] === 0) go(u);
if (hasCycle) return [];
order.reverse();
return order;
}
// Count number of paths from source s to every node
function countPathsDAG(adj, s) {
const order = topoSortKahn(adj);
if (order.length === 0) throw new Error('Graph has a cycle');
const ways = Array(adj.length).fill(0);
ways[s] = 1;
for (const u of order) for (const v of adj[u]) ways[v] += ways[u];
return ways;
}
// Longest path in DAG with weights on edges (assume weight[u][v])
function longestPathDAG(adj, weight, s) {
const n = adj.length; const order = topoSortKahn(adj);
if (order.length === 0) throw new Error('Graph has a cycle');
const dist = Array(n).fill(-Infinity); dist[s] = 0;
for (const u of order) if (dist[u] > -Infinity) {
for (const v of adj[u]) dist[v] = Math.max(dist[v], dist[u] + weight[u][v]);
}
return dist;
}