```ts
export function rollupTotals(
  nodes: Record<string, { value: number; parent: string }>,
): Record<string, number> {
  const totals: Record<string, number> = {};
  const nodeIds = Object.keys(nodes);
  const rootNodes = nodeIds.filter(id => nodes[id].parent === '');

  if (rootNodes.length !== 1) {
    throw new Error('Input must have exactly one root node.');
  }

  const visited = new Set<string>();

  function calculateTotal(nodeId: string): number {
    if (visited.has(nodeId)) {
      throw new Error('Cycle detected in the hierarchy.');
    }
    visited.add(nodeId);

    let total = nodes[nodeId].value;
    for (const other of nodeIds) {
      if (nodes[other].parent === nodeId) {
        total += calculateTotal(other);
      }
    }
    return total;
  }

  const rootId = rootNodes[0];
  totals[rootId] = calculateTotal(rootId);

  // Check if all nodes are reachable from the root
  for (const id of nodeIds) {
    if (!visited.has(id)) {
      throw new Error('Node cannot be reached from the root.');
    }
  }

  return totals;
}
```