|
|
import java.io.*; |
|
|
import java.util.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class clique2_fixb { |
|
|
static int n, m; |
|
|
|
|
|
public static void main(String[] args) throws Exception { |
|
|
if (args.length < 2) { |
|
|
System.err.println("Usage: java clique2_fixb <epsilon> <inputfile>"); |
|
|
} |
|
|
final double EPS = Double.parseDouble(args[0]); |
|
|
|
|
|
Scanner r; |
|
|
try { |
|
|
r = new Scanner(new FileReader(args[1])); |
|
|
} catch (IOException e) { |
|
|
System.err.println("Could not open " + args[1] + ". Falling back to stdin."); |
|
|
r = new Scanner(System.in); |
|
|
} |
|
|
|
|
|
n = r.nextInt(); |
|
|
m = r.nextInt(); |
|
|
|
|
|
List<Integer>[] adj = new ArrayList[n + 1]; |
|
|
for (int i = 1; i <= n; i++) adj[i] = new ArrayList<>(); |
|
|
for (int i = 0; i < m; i++) { |
|
|
int a = r.nextInt(), b = r.nextInt(); |
|
|
adj[a].add(b); |
|
|
adj[b].add(a); |
|
|
} |
|
|
r.close(); |
|
|
|
|
|
long t0 = System.nanoTime(); |
|
|
Result res = runLaplacianRMC(adj, EPS); |
|
|
long t1 = System.nanoTime(); |
|
|
|
|
|
System.out.printf(Locale.US, "%.6f, %d%n", res.bestSL, res.bestRoot); |
|
|
System.out.printf(Locale.US, "Runtime: %.3f ms%n", (t1 - t0) / 1_000_000.0); |
|
|
} |
|
|
|
|
|
static Result runLaplacianRMC(List<Integer>[] adj, double EPS) { |
|
|
|
|
|
int[] deg = new int[n + 1]; |
|
|
PriorityQueue<Pair> pq = new PriorityQueue<>(); |
|
|
for (int i = 1; i <= n; i++) { |
|
|
deg[i] = adj[i].size(); |
|
|
pq.add(new Pair(i, deg[i])); |
|
|
} |
|
|
Deque<Pair> stack = new ArrayDeque<>(n); |
|
|
while (!pq.isEmpty()) { |
|
|
Pair p = pq.poll(); |
|
|
if (p.degree != deg[p.node]) continue; |
|
|
stack.push(p); |
|
|
for (int v : adj[p.node]) { |
|
|
if (deg[v] > 0) { |
|
|
deg[v]--; |
|
|
pq.add(new Pair(v, deg[v])); |
|
|
} |
|
|
} |
|
|
deg[p.node] = 0; |
|
|
} |
|
|
|
|
|
|
|
|
DSU dsu = new DSU(n); |
|
|
boolean[] inGraph = new boolean[n + 1]; |
|
|
|
|
|
|
|
|
int[] d = new int[n + 1]; |
|
|
long[] sumNbrDeg = new long[n + 1]; |
|
|
|
|
|
|
|
|
long[] compEnergy = new long[n + 1]; |
|
|
|
|
|
|
|
|
int[] rootSeenStamp = new int[n + 1]; |
|
|
int stamp = 1; |
|
|
|
|
|
double bestSL = 0.0; |
|
|
int bestRoot = 0; |
|
|
|
|
|
while (!stack.isEmpty()) { |
|
|
Pair item = stack.pop(); |
|
|
int u = item.node; |
|
|
|
|
|
|
|
|
List<Integer> nbrs = new ArrayList<>(); |
|
|
for (int v : adj[u]) { |
|
|
if (inGraph[v]) nbrs.add(v); |
|
|
} |
|
|
|
|
|
|
|
|
int root = u; |
|
|
dsu.makeIfNeeded(u); |
|
|
for (int v : nbrs) { |
|
|
root = dsu.union(root, v); |
|
|
} |
|
|
root = dsu.find(root); |
|
|
|
|
|
|
|
|
long mergedEnergy = compEnergy[root]; |
|
|
if (!nbrs.isEmpty()) { |
|
|
long add = 0; |
|
|
for (int v : nbrs) { |
|
|
int rv = dsu.find(v); |
|
|
if (rootSeenStamp[rv] != stamp) { |
|
|
rootSeenStamp[rv] = stamp; |
|
|
add += compEnergy[rv]; |
|
|
} |
|
|
} |
|
|
stamp++; |
|
|
mergedEnergy = add; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inGraph[u] = true; |
|
|
|
|
|
|
|
|
compEnergy[root] = mergedEnergy; |
|
|
|
|
|
|
|
|
for (int v : nbrs) { |
|
|
|
|
|
int rBefore = dsu.find(u); |
|
|
int rOther = dsu.find(v); |
|
|
if (rBefore != rOther) { |
|
|
|
|
|
int rNew = dsu.union(u, v); |
|
|
root = rNew; |
|
|
} else { |
|
|
root = rBefore; |
|
|
} |
|
|
|
|
|
|
|
|
long du = d[u], dv = d[v]; |
|
|
long delta = (du - dv) * (du - dv); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long deg_u = du; |
|
|
delta += 2 * du * deg_u - 2 * sumNbrDeg[u] + deg_u; |
|
|
|
|
|
|
|
|
long deg_v = dv; |
|
|
delta += 2 * dv * deg_v - 2 * sumNbrDeg[v] + deg_v; |
|
|
|
|
|
compEnergy[root] += delta; |
|
|
|
|
|
|
|
|
|
|
|
sumNbrDeg[u] += dv; |
|
|
sumNbrDeg[v] += du; |
|
|
|
|
|
|
|
|
d[u] = (int) (du + 1); |
|
|
d[v] = (int) (dv + 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int x : adj[u]) if (inGraph[x] && dsu.find(x) == root) sumNbrDeg[x] += 1; |
|
|
for (int y : adj[v]) if (inGraph[y] && dsu.find(y) == root) sumNbrDeg[y] += 1; |
|
|
} |
|
|
|
|
|
|
|
|
int compRoot = dsu.find(u); |
|
|
int compSize = dsu.size[compRoot]; |
|
|
double sL = compSize / (compEnergy[compRoot] + EPS); |
|
|
if (sL > bestSL) { |
|
|
bestSL = sL; |
|
|
bestRoot = compRoot; |
|
|
} |
|
|
} |
|
|
|
|
|
Result out = new Result(); |
|
|
out.bestSL = bestSL; |
|
|
out.bestRoot = bestRoot; |
|
|
return out; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static class Result { |
|
|
double bestSL; |
|
|
int bestRoot; |
|
|
} |
|
|
|
|
|
static class Pair implements Comparable<Pair> { |
|
|
final int node, degree; |
|
|
Pair(int node, int degree) { this.node = node; this.degree = degree; } |
|
|
public int compareTo(Pair o) { |
|
|
if (degree != o.degree) return Integer.compare(degree, o.degree); |
|
|
return Integer.compare(node, o.node); |
|
|
} |
|
|
} |
|
|
|
|
|
static class DSU { |
|
|
final int[] parent; |
|
|
final int[] size; |
|
|
final boolean[] made; |
|
|
|
|
|
DSU(int n) { |
|
|
parent = new int[n + 1]; |
|
|
size = new int[n + 1]; |
|
|
made = new boolean[n + 1]; |
|
|
} |
|
|
void makeIfNeeded(int v) { |
|
|
if (!made[v]) { |
|
|
made[v] = true; |
|
|
parent[v] = v; |
|
|
size[v] = 1; |
|
|
} |
|
|
} |
|
|
int find(int v) { |
|
|
if (!made[v]) return v; |
|
|
if (parent[v] != v) parent[v] = find(parent[v]); |
|
|
return parent[v]; |
|
|
} |
|
|
int union(int a, int b) { |
|
|
makeIfNeeded(a); |
|
|
makeIfNeeded(b); |
|
|
int ra = find(a), rb = find(b); |
|
|
if (ra == rb) return ra; |
|
|
if (size[ra] < size[rb]) { int t = ra; ra = rb; rb = t; } |
|
|
parent[rb] = ra; |
|
|
size[ra] += size[rb]; |
|
|
return ra; |
|
|
} |
|
|
} |
|
|
} |
|
|
|