diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..1434239 Binary files /dev/null and b/.DS_Store differ diff --git a/2SAT/Readme.md b/2SAT/Readme.md index b59e2c3..df59919 100644 --- a/2SAT/Readme.md +++ b/2SAT/Readme.md @@ -71,6 +71,14 @@ public void addNand(int x, boolean f, int y, boolean g) ならし $O(1)$ +### set + +```java +public void set(int x, boolean f) +``` + +`(x[i] = f)` というクローズを足します。 + ### satisfiable ```java diff --git a/2SAT/TwoSAT.java b/2SAT/TwoSAT.java index a7a7c24..48d05f9 100644 --- a/2SAT/TwoSAT.java +++ b/2SAT/TwoSAT.java @@ -29,6 +29,10 @@ public void addImplication(int x, boolean f, int y, boolean g) { public void addNand(int x, boolean f, int y, boolean g) { addClause(x, !f, y, !g); } + public void set(int x, boolean f){ + addClause(x, f, x, f); + } + public boolean satisfiable() { hasCalledSatisfiable = true; @@ -51,7 +55,7 @@ public boolean[] answer() { } private void rangeCheck(int x) { - if (0 < x || x >= n) { + if (x < 0 || x >= n) { throw new IndexOutOfBoundsException( String.format("Index %d out of bounds for length %d", x, n) ); diff --git a/ArticulationPoint/ArticulationPoint.java b/ArticulationPoint/ArticulationPoint.java new file mode 100644 index 0000000..a28f680 --- /dev/null +++ b/ArticulationPoint/ArticulationPoint.java @@ -0,0 +1,37 @@ +class ArticulationPoints{ + private static int time; + private static int[] disc, low; + private static boolean[] visited; + + public static boolean[] articulationPoints(int[][] graph){ + int NumberOfNodes = graph.length; + + time = -1; + disc = new int[NumberOfNodes]; + low = new int[NumberOfNodes]; + visited = new boolean[NumberOfNodes]; + java.util.Arrays.fill(low, 2*NumberOfNodes); + + boolean[] isAP = new boolean[NumberOfNodes]; + for(int i = 0; i< NumberOfNodes; i++)if(!visited[i] && graph[i] != null)apUtil(graph, isAP, i, -1); + return isAP; + } + + private static void apUtil(int[][] g, boolean[] isAP, int u, int p){ + visited[u] = true; + disc[u] = low[u] = ++time; + + int childCount = 0; + for(int v:g[u]){ + if(!visited[v]){ + apUtil(g, isAP, v, u); + + low[u] = Math.min(low[u], low[v]); + childCount++; + + if(p != -1 && low[v] >= disc[u])isAP[u] = true; + }else if(v != p)low[u] = Math.min(low[u], disc[v]); + } + if(p == -1 && childCount > 1)isAP[u] = true; + } +} diff --git a/ArticulationPoint/ArticulationPointsUsage.java b/ArticulationPoint/ArticulationPointsUsage.java new file mode 100644 index 0000000..4621622 --- /dev/null +++ b/ArticulationPoint/ArticulationPointsUsage.java @@ -0,0 +1,20 @@ +class ArticulationPointsUsage{ + //Usage example ArticulationPoint.articulationPoints() + public static void articulationPointsUsage(){ + int N = 6, E = 6; + int[] from = new int[]{0,1,1,2,2,4}; + int[] to = new int[]{5,4,5,3,4,5}; + + int[][] graph = GraphBuilder.makeGraph(N, E, from, to, true); + boolean[] articulationPoint = ArticulationPoints.articulationPoints(graph); + + for(int i = 0; i< N; i++){ + if(articulationPoint[i]) + System.out.println(i+" is an articulation point"); + } + } + + public static void main(String[] args){ + articulationPointsUsage(); + } +} diff --git a/ArticulationPoint/README.md b/ArticulationPoint/README.md new file mode 100644 index 0000000..5bc1ff8 --- /dev/null +++ b/ArticulationPoint/README.md @@ -0,0 +1,19 @@ +# Class ArticulationPoint + +Given a graph generated using GraphBuilder.makeGraph(), this class finds all articulation points in graph. Since GraphBuilder assumes 0-based indexing, this class also works assuming 0-based indexing. + +All members of this class are static, so no constructor defined. + +## Methods + +### articulationPoints +``` +public static boolean[] articulationPoints(int[][] graph) +``` +Returns boolean array of length $N$ where $N$ denotes the number of nodes in given graph, i-th element in this boolean array denotes whether i-th node in given graph is an articulation point or not. + +** Constraints ** +* graph is generated using GraphBuilder.makeGraph(), so all constraints applicable to makeGraph method applies. + +** Computational complexity ** +* $ O (NumberOfNodes + NumberOfEdges) $ diff --git a/Bridge/Bridge.java b/Bridge/Bridge.java new file mode 100644 index 0000000..a101de8 --- /dev/null +++ b/Bridge/Bridge.java @@ -0,0 +1,44 @@ +class Bridge{ + private static int time; + private static int[] disc, low; + private static boolean[] visited; + + public static boolean[] bridges(int[][][] graphWithEdgeInfo){ + int edgeCount = 0; + for(int[][] row:graphWithEdgeInfo)edgeCount += row.length; + return bridges(graphWithEdgeInfo, edgeCount/2); + } + + public static boolean[] bridges(int[][][] graphWithEdgeInfo, int edgeCount){ + int NumberOfNodes = graphWithEdgeInfo.length; + + time = -1; + disc = new int[NumberOfNodes]; + low = new int[NumberOfNodes]; + visited = new boolean[NumberOfNodes]; + java.util.Arrays.fill(low, 2*NumberOfNodes); + + boolean[] isBridge = new boolean[edgeCount]; + for(int i = 0; i< NumberOfNodes; i++)if(!visited[i] && graphWithEdgeInfo[i] != null)bridgeUtil(graphWithEdgeInfo, isBridge, i, -1); + return isBridge; + } + + private static void bridgeUtil(int[][][] g, boolean[] isBridge, int u, int parentEdge){ + visited[u] = true; + disc[u] = low[u] = ++time; + + for(int[] edge: g[u]){ + int v = edge[0], edgeIndex = edge[1]; + if(parentEdge == edgeIndex)continue; + + if(visited[v]) + low[u] = Math.min(low[u], disc[v]); + else{ + bridgeUtil(g, isBridge, v, edgeIndex); + low[u] = Math.min(low[u], low[v]); + + if(low[v] > disc[u])isBridge[edgeIndex] = true; + } + } + } +} diff --git a/Bridge/BridgeUsage.java b/Bridge/BridgeUsage.java new file mode 100644 index 0000000..c5b5342 --- /dev/null +++ b/Bridge/BridgeUsage.java @@ -0,0 +1,20 @@ +class BridgeUsage{ + //Usage Example Bridge.bridges() + public static void bridgesUsage(){ + int N = 6, E = 9; + int[] from = new int[]{0,1,1,2,2,4,4,3,1}; + int[] to = new int[]{5,4,5,3,4,5,4,2,4}; + + int[][][] graph = GraphBuilder.makeGraphWithEdgeInfo(N, E, from, to, true); + boolean[] isBridge = Bridge.bridges(graph); + + for(int i = 0; i< E; i++){ + if(isBridge[i]) + System.out.println(from[i] + " - " + to[i] + " is a bridge"); + } + } + + public static void main(String[] args){ + bridgesUsage(); + } +} diff --git a/Bridge/README.md b/Bridge/README.md new file mode 100644 index 0000000..08594c6 --- /dev/null +++ b/Bridge/README.md @@ -0,0 +1,32 @@ +# Class Bridge + +Given an undirected graph generated using GraphBuilder.makeGraphWithEdgeInfo(), this class finds all bridges in graph. Since GraphBuilder assumes 0-based indexing, this class also works assuming 0-based indexing. This class can handle multi-edges as well as self loops. + +All members of this class are static, so no constructor defined. + +## Methods + +### bridges +``` +public static boolean[] bridges(int[][][] graphWithEdgeInfo, int edgeCount) +``` +Returns boolean array of length $edgeCount$ where $EdgeCount$ denotes the number of edges in given graph, i-th element in this boolean array denotes whether i-th edge in given graph is a bridge or not. The edges are numbered as per their order in input, defined in GraphBuilder.makeGraphWithEdgeInfo() + +** Constraints ** +* graph is generated using GraphBuilder.makeGraphWithEdgeInfo(), so all constraints applicable to makeGraphWithEdgeInfo method applies. +* edgeCount = Number of edges specified in GraphBuilder.makeGraphWithEdgeInfo() + +** Computational complexity ** +* $ O (NumberOfNodes + NumberOfEdges) $ + + +``` +public static boolean[] bridges(int[][][] graphWithEdgeInfo) +``` +This method computed the number of edges in specified graph, and invokes ```boolean[] bridges(int[][][] graphWithEdgeInfo, int edgeCount)``` to return boolean array, i-th element in this boolean array denotes whether i-th edge in given graph is a bridge or not. + +** Constraints ** +* graph is generated using GraphBuilder.makeGraphWithEdgeInfo(), so all constraints applicable to makeGraphWithEdgeInfo method applies. + +** Computational complexity ** +* $ O (NumberOfNodes + NumberOfEdges) $ diff --git a/ContestIO/ContestPrinter.java b/ContestIO/ContestPrinter.java new file mode 100644 index 0000000..545045d --- /dev/null +++ b/ContestIO/ContestPrinter.java @@ -0,0 +1,140 @@ +class ContestPrinter extends java.io.PrintWriter{ + public ContestPrinter(java.io.PrintStream stream){ + super(stream); + } + public ContestPrinter(java.io.File file) throws java.io.FileNotFoundException{ + super(new java.io.PrintStream(file)); + } + public ContestPrinter(){ + super(System.out); + } + + private static String dtos(double x, int n) { + StringBuilder sb = new StringBuilder(); + if(x < 0){ + sb.append('-'); + x = -x; + } + x += Math.pow(10, -n)/2; + sb.append((long)x); + sb.append("."); + x -= (long)x; + for(int i = 0;i < n;i++){ + x *= 10; + sb.append((int)x); + x -= (int)x; + } + return sb.toString(); + } + + @Override + public void print(float f){ + super.print(dtos(f, 20)); + } + @Override + public void println(float f){ + super.println(dtos(f, 20)); + } + @Override + public void print(double d){ + super.print(dtos(d, 20)); + } + @Override + public void println(double d){ + super.println(dtos(d, 20)); + } + + + + public void printArray(int[] array, String separator){ + int n = array.length; + if(n==0){ + super.println(); + return; + } + for(int i=0; i void printArray(T[] array, String separator){ + int n = array.length; + if(n==0){ + super.println(); + return; + } + for(int i=0; i void printArray(T[] array){ + this.printArray(array, " "); + } + public void printArray(T[] array, String separator, java.util.function.UnaryOperator map){ + int n = array.length; + if(n==0){ + super.println(); + return; + } + for(int i=0; i void printArray(T[] array, java.util.function.UnaryOperator map){ + this.printArray(array, " ", map); + } +} diff --git a/ContestIO/ContestScanner.java b/ContestIO/ContestScanner.java new file mode 100644 index 0000000..3d3c80e --- /dev/null +++ b/ContestIO/ContestScanner.java @@ -0,0 +1,186 @@ +class ContestScanner { + private final java.io.InputStream in; + private final byte[] buffer = new byte[1024]; + private int ptr = 0; + private int buflen = 0; + + private static final long LONG_MAX_TENTHS = 922337203685477580L; + private static final int LONG_MAX_LAST_DIGIT = 7; + private static final int LONG_MIN_LAST_DIGIT = 8; + + public ContestScanner(java.io.InputStream in){ + this.in = in; + } + public ContestScanner(java.io.File file) throws java.io.FileNotFoundException { + this(new java.io.BufferedInputStream(new java.io.FileInputStream(file))); + } + public ContestScanner(){ + this(System.in); + } + + private boolean hasNextByte() { + if (ptr < buflen) { + return true; + }else{ + ptr = 0; + try { + buflen = in.read(buffer); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + if (buflen <= 0) { + return false; + } + } + return true; + } + private int readByte() { + if (hasNextByte()) return buffer[ptr++]; else return -1; + } + private static boolean isPrintableChar(int c) { + return 33 <= c && c <= 126; + } + public boolean hasNext() { + while(hasNextByte() && !isPrintableChar(buffer[ptr])) ptr++; + return hasNextByte(); + } + public String next() { + if (!hasNext()) throw new java.util.NoSuchElementException(); + StringBuilder sb = new StringBuilder(); + int b = readByte(); + while(isPrintableChar(b)) { + sb.appendCodePoint(b); + b = readByte(); + } + return sb.toString(); + } + + public long nextLong() { + if (!hasNext()) throw new java.util.NoSuchElementException(); + long n = 0; + boolean minus = false; + int b = readByte(); + if (b == '-') { + minus = true; + b = readByte(); + } + if (b < '0' || '9' < b) { + throw new NumberFormatException(); + } + while (true) { + if ('0' <= b && b <= '9') { + int digit = b - '0'; + if (n >= LONG_MAX_TENTHS) { + if (n == LONG_MAX_TENTHS) { + if (minus) { + if (digit <= LONG_MIN_LAST_DIGIT) { + n = -n * 10 - digit; + b = readByte(); + if (!isPrintableChar(b)) { + return n; + } else if (b < '0' || '9' < b) { + throw new NumberFormatException( + String.format("%d%s... is not number", n, Character.toString(b)) + ); + } + } + } else { + if (digit <= LONG_MAX_LAST_DIGIT) { + n = n * 10 + digit; + b = readByte(); + if (!isPrintableChar(b)) { + return n; + } else if (b < '0' || '9' < b) { + throw new NumberFormatException( + String.format("%d%s... is not number", n, Character.toString(b)) + ); + } + } + } + } + throw new ArithmeticException( + String.format("%s%d%d... overflows long.", minus ? "-" : "", n, digit) + ); + } + n = n * 10 + digit; + }else if(b == -1 || !isPrintableChar(b)){ + return minus ? -n : n; + }else{ + throw new NumberFormatException(); + } + b = readByte(); + } + } + public int nextInt() { + long nl = nextLong(); + if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE) throw new NumberFormatException(); + return (int) nl; + } + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long[] nextLongArray(int length){ + long[] array = new long[length]; + for(int i=0; i 0) { + long c = a / b; + long d; + d = a; + a = b; + b = d % b; + d = p; + p = q; + q = d - c * q; + } + return p < 0 ? p + mod : p; + } + + private final int rank2; + public final long[] root; + public final long[] iroot; + public final long[] rate2; + public final long[] irate2; + public final long[] rate3; + public final long[] irate3; + + public FftInfo(int g, int mod) { + rank2 = bsfConstexpr(mod - 1); + root = new long[rank2 + 1]; + iroot = new long[rank2 + 1]; + rate2 = new long[Math.max(0, rank2 - 2 + 1)]; + irate2 = new long[Math.max(0, rank2 - 2 + 1)]; + rate3 = new long[Math.max(0, rank2 - 3 + 1)]; + irate3 = new long[Math.max(0, rank2 - 3 + 1)]; + + root[rank2] = pow(g, (mod - 1) >> rank2, mod); + iroot[rank2] = inv(root[rank2], mod); + for (int i = rank2 - 1; i >= 0; i--) { + root[i] = root[i + 1] * root[i + 1] % mod; + iroot[i] = iroot[i + 1] * iroot[i + 1] % mod; + } + + { + long prod = 1, iprod = 1; + for (int i = 0; i <= rank2 - 2; i++) { + rate2[i] = root[i + 2] * prod % mod; + irate2[i] = iroot[i + 2] * iprod % mod; + prod = prod * iroot[i + 2] % mod; + iprod = iprod * root[i + 2] % mod; + } + } + { + long prod = 1, iprod = 1; + for (int i = 0; i <= rank2 - 3; i++) { + rate3[i] = root[i + 3] * prod % mod; + irate3[i] = iroot[i + 3] * iprod % mod; + prod = prod * iroot[i + 3] % mod; + iprod = iprod * root[i + 3] % mod; + } + } + } + }; + /** * Garner's algorithm. * @@ -104,87 +172,65 @@ private static long garner(long[] c, int[] mods) { return cnst[n - 1]; } - /** - * Pre-calculation for NTT. - * - * @param mod NTT Prime. - * @param g Primitive root of mod. - * @return Pre-calculation table. - */ - private static long[] sumE(int mod, int g) { - long[] sum_e = new long[30]; - long[] es = new long[30]; - long[] ies = new long[30]; - int cnt2 = Integer.numberOfTrailingZeros(mod - 1); - long e = pow(g, (mod - 1) >> cnt2, mod); - long ie = pow(e, mod - 2, mod); - for (int i = cnt2; i >= 2; i--) { - es[i - 2] = e; - ies[i - 2] = ie; - e = e * e % mod; - ie = ie * ie % mod; - } - long now = 1; - for (int i = 0; i < cnt2 - 2; i++) { - sum_e[i] = es[i] * now % mod; - now = now * ies[i] % mod; - } - return sum_e; - } - - /** - * Pre-calculation for inverse NTT. - * - * @param mod Mod. - * @param g Primitive root of mod. - * @return Pre-calculation table. - */ - private static long[] sumIE(int mod, int g) { - long[] sum_ie = new long[30]; - long[] es = new long[30]; - long[] ies = new long[30]; - - int cnt2 = Integer.numberOfTrailingZeros(mod - 1); - long e = pow(g, (mod - 1) >> cnt2, mod); - long ie = pow(e, mod - 2, mod); - for (int i = cnt2; i >= 2; i--) { - es[i - 2] = e; - ies[i - 2] = ie; - e = e * e % mod; - ie = ie * ie % mod; - } - long now = 1; - for (int i = 0; i < cnt2 - 2; i++) { - sum_ie[i] = ies[i] * now % mod; - now = now * es[i] % mod; - } - return sum_ie; - } - /** * Inverse NTT. * * @param a Target array. - * @param sumIE Pre-calculation table. + * @param g Primitive root of mod. * @param mod NTT Prime. */ - private static void butterflyInv(long[] a, long[] sumIE, int mod) { + private static void butterflyInv(long[] a, int g, int mod) { int n = a.length; int h = ceilPow2(n); - for (int ph = h; ph >= 1; ph--) { - int w = 1 << (ph - 1), p = 1 << (h - ph); - long inow = 1; - for (int s = 0; s < w; s++) { - int offset = s << (h - ph + 1); - for (int i = 0; i < p; i++) { - long l = a[i + offset]; - long r = a[i + offset + p]; - a[i + offset] = (l + r) % mod; - a[i + offset + p] = (mod + l - r) * inow % mod; + FftInfo info = new FftInfo(g, mod); + + int len = h; // a[i, i+(n>>len), i+2*(n>>len), ..] is transformed + while (len > 0) { + if (len == 1) { + int p = 1 << (h - len); + long irot = 1; + for (int s = 0; s < (1 << (len - 1)); s++) { + int offset = s << (h - len + 1); + for (int i = 0; i < p; i++) { + long l = a[i + offset]; + long r = a[i + offset + p]; + a[i + offset] = (l + r) % mod; + a[i + offset + p] = (mod + l - r) % mod * irot % mod; + } + if (s + 1 != (1 << (len - 1))) { + irot *= info.irate2[Integer.numberOfTrailingZeros(~s)]; + irot %= mod; + } } - int x = Integer.numberOfTrailingZeros(~s); - inow = inow * sumIE[x] % mod; + len--; + } else { + // 4-base + int p = 1 << (h - len); + long irot = 1, iimag = info.iroot[2]; + for (int s = 0; s < (1 << (len - 2)); s++) { + long irot2 = irot * irot % mod; + long irot3 = irot2 * irot % mod; + int offset = s << (h - len + 2); + for (int i = 0; i < p; i++) { + long a0 = 1L * a[i + offset + 0 * p]; + long a1 = 1L * a[i + offset + 1 * p]; + long a2 = 1L * a[i + offset + 2 * p]; + long a3 = 1L * a[i + offset + 3 * p]; + + long a2na3iimag = 1L * (mod + a2 - a3) % mod * iimag % mod; + + a[i + offset] = (a0 + a1 + a2 + a3) % mod; + a[i + offset + 1 * p] = (a0 + (mod - a1) + a2na3iimag) % mod * irot % mod; + a[i + offset + 2 * p] = (a0 + a1 + (mod - a2) + (mod - a3)) % mod * irot2 % mod; + a[i + offset + 3 * p] = (a0 + (mod - a1) + (mod - a2na3iimag)) % mod * irot3 % mod; + } + if (s + 1 != (1 << (len - 2))) { + irot *= info.irate3[Integer.numberOfTrailingZeros(~s)]; + irot %= mod; + } + } + len -= 2; } } } @@ -193,26 +239,61 @@ private static void butterflyInv(long[] a, long[] sumIE, int mod) { * Inverse NTT. * * @param a Target array. - * @param sumE Pre-calculation table. + * @param g Primitive root of mod. * @param mod NTT Prime. */ - private static void butterfly(long[] a, long[] sumE, int mod) { + private static void butterfly(long[] a, int g, int mod) { int n = a.length; int h = ceilPow2(n); - for (int ph = 1; ph <= h; ph++) { - int w = 1 << (ph - 1), p = 1 << (h - ph); - long now = 1; - for (int s = 0; s < w; s++) { - int offset = s << (h - ph + 1); - for (int i = 0; i < p; i++) { - long l = a[i + offset]; - long r = a[i + offset + p] * now % mod; - a[i + offset] = (l + r) % mod; - a[i + offset + p] = (l - r + mod) % mod; + FftInfo info = new FftInfo(g, mod); + + int len = 0; // a[i, i+(n>>len), i+2*(n>>len), ..] is transformed + while (len < h) { + if (h - len == 1) { + int p = 1 << (h - len - 1); + long rot = 1; + for (int s = 0; s < (1 << len); s++) { + int offset = s << (h - len); + for (int i = 0; i < p; i++) { + long l = a[i + offset]; + long r = a[i + offset + p] * rot % mod; + a[i + offset] = (l + r) % mod; + a[i + offset + p] = (l + mod - r) % mod; + } + if (s + 1 != (1 << len)) { + rot *= info.rate2[Integer.numberOfTrailingZeros(~s)]; + rot %= mod; + } + } + len++; + } else { + // 4-base + int p = 1 << (h - len - 2); + long rot = 1, imag = info.root[2]; + for (int s = 0; s < (1 << len); s++) { + long rot2 = rot * rot % mod; + long rot3 = rot2 * rot % mod; + int offset = s << (h - len); + for (int i = 0; i < p; i++) { + long mod2 = 1L * mod * mod; + long a0 = 1L * a[i + offset]; + long a1 = 1L * a[i + offset + p] * rot % mod; + long a2 = 1L * a[i + offset + 2 * p] * rot2 % mod; + long a3 = 1L * a[i + offset + 3 * p] * rot3 % mod; + long a1na3imag = 1L * (a1 + mod2 - a3) % mod * imag % mod; + long na2 = mod2 - a2; + a[i + offset] = (a0 + a2 + a1 + a3) % mod; + a[i + offset + 1 * p] = (a0 + a2 + (2 * mod2 - (a1 + a3))) % mod; + a[i + offset + 2 * p] = (a0 + na2 + a1na3imag) % mod; + a[i + offset + 3 * p] = (a0 + na2 + (mod2 - a1na3imag)) % mod; + } + if (s + 1 != (1 << len)) { + rot *= info.rate3[Integer.numberOfTrailingZeros(~s)]; + rot %= mod; + } } - int x = Integer.numberOfTrailingZeros(~s); - now = now * sumE[x] % mod; + len += 2; } } } @@ -241,15 +322,13 @@ public static long[] convolution(long[] a, long[] b, int mod) { } int g = primitiveRoot(mod); - long[] sume = sumE(mod, g); - long[] sumie = sumIE(mod, g); - butterfly(a, sume, mod); - butterfly(b, sume, mod); + butterfly(a, g, mod); + butterfly(b, g, mod); for (int i = 0; i < z; i++) { a[i] = a[i] * b[i] % mod; } - butterflyInv(a, sumie, mod); + butterflyInv(a, g, mod); a = java.util.Arrays.copyOf(a, n + m - 1); long iz = pow(z, mod - 2, mod); diff --git a/Convolution/README.md b/Convolution/README.md index ac3e741..df7ec2c 100644 --- a/Convolution/README.md +++ b/Convolution/README.md @@ -15,7 +15,7 @@ public static void main(String[] args) { int mod = 998244353; long[] a = { 1, 2, 3, 4, 5 }; long[] b = { 6, 7, 8 }; - // 畳み込みを計算しまqす。a.length + b.length - 1 の配列が帰ります。 + // 畳み込みを計算します。a.length + b.length - 1 の配列が帰ります。 long[] ret = Convolution.convolution(a, b, mod); System.out.println(Arrays.toString(ret)); } diff --git a/DSU/README.md b/DSU/README.md index 3d8ccdd..1517880 100644 --- a/DSU/README.md +++ b/DSU/README.md @@ -8,7 +8,7 @@ を $O(α(N))$ 時間で処理することが出来ます。 -また、内部的に各連結成分ごとに代表となる頂点を 11 つ持っています。辺の追加により連結成分がマージされる時、新たな代表元は元の連結成分の代表元のうちどちらかになります。 +また、内部的に各連結成分ごとに代表となる頂点を 1 つ持っています。辺の追加により連結成分がマージされる時、新たな代表元は元の連結成分の代表元のうちどちらかになります。 ## コンストラクタ ### DSU diff --git a/FenwickTree/FenwickTree.java b/FenwickTree/FenwickTree.java index 53aecd7..c896ad9 100644 --- a/FenwickTree/FenwickTree.java +++ b/FenwickTree/FenwickTree.java @@ -16,6 +16,10 @@ public FenwickTree(long[] data) { build(data); } + public void set(int p, long x){ + add(p, x - get(p)); + } + public void add(int p, long x){ assert(0<=p && p<_n); p++; @@ -29,6 +33,10 @@ public long sum(int l, int r){ return sum(r)-sum(l); } + public long get(int p){ + return sum(p, p+1); + } + private long sum(int r){ long s = 0; while(r>0){ diff --git a/FenwickTree/README.md b/FenwickTree/README.md index bcbe624..37723ea 100644 --- a/FenwickTree/README.md +++ b/FenwickTree/README.md @@ -32,6 +32,19 @@ public void add(int p, long x) 配列の第$p$要素に$x$を加える. すなわち, `a[p] += x` のこと. 計算量: $O(\log N)$ +### set +```java +public void set(int p, long x) +``` +配列の第$p$要素を$x$に変更する. すなわち, `a[p] = x` のこと. +計算量: $O(\log N)$ + +### get +```java +public long get(int p) +``` +配列の第$p$要素を取得する. 計算量: $O(\log n)$ + ### sum ```java public long sum(int l, int r) diff --git a/GraphBuilder/GraphBuilder.java b/GraphBuilder/GraphBuilder.java new file mode 100644 index 0000000..95a4be8 --- /dev/null +++ b/GraphBuilder/GraphBuilder.java @@ -0,0 +1,31 @@ +class GraphBuilder{ + public static int[][] makeGraph(int NumberOfNodes, int NumberOfEdges, int[] from, int[] to, boolean undirected){ + int[][] graph = new int[NumberOfNodes][]; + int[] outdegree = new int[NumberOfNodes]; + for(int i = 0; i< NumberOfEdges; i++){ + outdegree[from[i]]++; + if(undirected)outdegree[to[i]]++; + } + for(int i = 0; i< NumberOfNodes; i++)graph[i] = new int[outdegree[i]]; + for(int i = 0; i< NumberOfEdges; i++){ + graph[from[i]][--outdegree[from[i]]] = to[i]; + if(undirected)graph[to[i]][--outdegree[to[i]]] = from[i]; + } + return graph; + } + + public static int[][][] makeGraphWithEdgeInfo(int NumberOfNodes, int NumberOfEdges, int[] from, int[] to, boolean undirected){ + int[][][] graph = new int[NumberOfNodes][][]; + int[] outdegree = new int[NumberOfNodes]; + for(int i = 0; i< NumberOfEdges; i++){ + outdegree[from[i]]++; + if(undirected)outdegree[to[i]]++; + } + for(int i = 0; i< NumberOfNodes; i++)graph[i] = new int[outdegree[i]][]; + for(int i = 0; i< NumberOfEdges; i++){ + graph[from[i]][--outdegree[from[i]]] = new int[]{to[i], i, 0}; + if(undirected)graph[to[i]][--outdegree[to[i]]] = new int[]{from[i], i, 1}; + } + return graph; + } +} diff --git a/GraphBuilder/GraphBuilderUsage.java b/GraphBuilder/GraphBuilderUsage.java new file mode 100644 index 0000000..cc38c57 --- /dev/null +++ b/GraphBuilder/GraphBuilderUsage.java @@ -0,0 +1,34 @@ +class GraphBuilderUsage{ + //Usage Exmaple + public static void makeGraphUsage(){ + int N = 6, E = 8; + int[] from = new int[]{0,0,0,1,1,2,2,4}; + int[] to = new int[]{2,4,5,4,5,3,4,5}; + + int[][] graph = GraphBuilder.makeGraph(N, E, from, to, true); + for(int i = 0; i< N; i++){ + System.out.print(i+": "); + for(int j:graph[i])System.out.print(j+" "); + System.out.println(); + } + } + + //Usage Example + public static void makeGraphWithEdgeInfoUsage(){ + int N = 6, E = 8; + int[] from = new int[]{0,0,0,1,1,2,2,4}; + int[] to = new int[]{2,4,5,4,5,3,4,5}; + + int[][][] graph = GraphBuilder.makeGraphWithEdgeInfo(N, E, from, to, true); + for(int i = 0; i< N; i++){ + System.out.print(i+": "); + for(int[] j:graph[i])System.out.print("["+j[0]+", "+j[1]+", "+j[2]+"],"); + System.out.println(); + } + } + + public static void main(String[] args){ + makeGraphUsage(); + makeGraphWithEdgeInfoUsage(); + } +} diff --git a/GraphBuilder/README.md b/GraphBuilder/README.md new file mode 100644 index 0000000..22fa59c --- /dev/null +++ b/GraphBuilder/README.md @@ -0,0 +1,40 @@ +# Class GraphBuilder + +Creates graph in form of jagged arrays (as compared to Arraylist) for directed as well as undirected graphs from given set of edges in 0 indexing. + +All members of this class are static, so no constructor defined. + +## Methods + +### makeGraph +``` +public static int[][] makeGraph(int NumberOfNodes, int NumberOfEdges, int[] from, int[] to, boolean undirected) +``` +Returns the adjacency list representation of graph with specified Number of Nodes and Edges and the edges specified in from and to arrays in 0-based indexing. If undirected is set to true, the edges are deemed to be undirected, otherwise directed. + +```graph[u]``` is the array of vertices having an edge outgoing from vertex u. + +** Constraints ** +* 0 \leq from[i], to[i] < NumberOfNodes +* from.length = to.length = NumberOfEdges + +** Computational complexity ** +* $ O (NumberOfNodes + NumberOfEdges) $ + +### makeGraphWithEdgeInfo +``` +public static int[][][] makeGraphWithEdgeInfo(int NumberOfNodes, int NumberOfEdges, int[] from, int[] to, boolean undirected) +``` + +Returns the adjacency list representation of graph with specified Number of Nodes and Edges and the edges specified in from and to arrays in 0-based indexing. If undirected is set to true, the edges are deemed to be undirected, otherwise directed. This method also returns useful information about edge index and edge direction. + +```graph[u]``` is the array of tuples ```[v, idx, direction]``` where edge indexed ```idx``` is between node ```u``` and node ```v``` and direction takes value either $0$ or $1$. +- If direction is $0$, edge indexed ```idx``` is from ```u``` to ```v``` +- If direction is $1$, edge indexed ```idx``` is from ```v``` to ```u``` + +** Constraints ** +* 0 \leq from[i], to[i] < NumberOfNodes +* from.length = to.length = NumberOfEdges + +** Computational complexity ** +* $ O (NumberOfNodes + NumberOfEdges) $ diff --git a/LazySegTree/.gitignore b/LazySegTree/.gitignore new file mode 100644 index 0000000..0a8eadb --- /dev/null +++ b/LazySegTree/.gitignore @@ -0,0 +1,3 @@ +test/answer +test/in +test/out \ No newline at end of file diff --git a/LazySegTree/LazySegTree.java b/LazySegTree/LazySegTree.java index 9ec3a2a..ce09cfd 100644 --- a/LazySegTree/LazySegTree.java +++ b/LazySegTree/LazySegTree.java @@ -247,17 +247,36 @@ public void setIndent(int newIndent) { @Override public String toString() { - return toString(1, 0); + return toSimpleString(); } - private String toString(int k, int sp) { - if (k >= N) return indent(sp) + Dat[k]; + private S[] simulatePushAll() { + S[] simDat = java.util.Arrays.copyOf(Dat, 2 * N); + F[] simLaz = java.util.Arrays.copyOf(Laz, 2 * N); + for (int k = 1; k < N; k++) { + if (simLaz[k] == Id) continue; + int lk = k << 1 | 0, rk = k << 1 | 1; + simDat[lk] = Mapping.apply(simLaz[k], simDat[lk]); + simDat[rk] = Mapping.apply(simLaz[k], simDat[rk]); + if (lk < N) simLaz[lk] = Composition.apply(simLaz[k], simLaz[lk]); + if (rk < N) simLaz[rk] = Composition.apply(simLaz[k], simLaz[rk]); + simLaz[k] = Id; + } + return simDat; + } + + public String toDetailedString() { + return toDetailedString(1, 0, simulatePushAll()); + } + + private String toDetailedString(int k, int sp, S[] dat) { + if (k >= N) return indent(sp) + dat[k]; String s = ""; - s += toString(k << 1 | 1, sp + indent); + s += toDetailedString(k << 1 | 1, sp + indent, dat); s += "\n"; - s += indent(sp) + Dat[k] + "/" + Laz[k]; + s += indent(sp) + dat[k]; s += "\n"; - s += toString(k << 1 | 0, sp + indent); + s += toDetailedString(k << 1 | 0, sp + indent, dat); return s; } @@ -266,4 +285,16 @@ private static String indent(int n) { while (n --> 0) sb.append(' '); return sb.toString(); } + + public String toSimpleString() { + S[] dat = simulatePushAll(); + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0; i < N; i++) { + sb.append(dat[i + N]); + if (i < N - 1) sb.append(',').append(' '); + } + sb.append(']'); + return sb.toString(); + } } \ No newline at end of file diff --git a/LazySegTree/test/Gen.java b/LazySegTree/test/Gen.java new file mode 100644 index 0000000..174ae8c --- /dev/null +++ b/LazySegTree/test/Gen.java @@ -0,0 +1,101 @@ +package test; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Random; + +public class Gen { + static final int TESTCASE_NUM = 100; + + static final Random rnd = new Random(System.nanoTime()); + + static final int N_MIN = 1; + static final int N_MAX = 5000; + static final int Q_MIN = 1; + static final int Q_MAX = 10000; + static final int A_MIN = -1000000000; + static final int A_MAX = 1000000000; + static final int V_MIN = -1000000000; + static final int V_MAX = 1000000000; + static final int QUERY_MIN = 0; + static final int QUERY_MAX = 7; + + public static void main(String[] args) { + for (int i = 0; i < TESTCASE_NUM; i++) { + String fileName = String.format("LazySegTree/test/in/testcase_%d", i); + try (PrintWriter out = new PrintWriter(new File(fileName))) { + gen(out); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + public static void gen(PrintWriter out) { + final int N = rndIntClosedRange(N_MIN, N_MAX); + final int Q = rndIntClosedRange(Q_MIN, Q_MAX); + final int P_MIN = 0, P_MAX = N - 1; + final int L_MIN = 0, L_MAX = N; + final int R_MIN = 0, R_MAX = N; + out.print(N); out.print(' '); out.print(Q); out.print('\n'); + final int[] A = new int[N]; + for (int i = 0; i < N; i++) { + A[i] = rndIntClosedRange(A_MIN, A_MAX); + out.print(A[i]); + if (i < N - 1) { + out.print(' '); + } else { + out.print('\n'); + } + } + for (int i = 0; i < Q; i++) { + int type = rndIntClosedRange(QUERY_MIN, QUERY_MAX); + out.print(type); + if (type == 0) { // set + int p = rndIntClosedRange(P_MIN, P_MAX); + int v = rndIntClosedRange(V_MIN, V_MAX); + out.print(' ');out.print(p);out.print(' ');out.print(v);out.print('\n'); + } else if (type == 1) { // apply + int p = rndIntClosedRange(P_MIN, P_MAX); + int v = rndIntClosedRange(V_MIN, V_MAX); + out.print(' ');out.print(p);out.print(' ');out.print(v);out.print('\n'); + } else if (type == 2) { // apply + int l = rndIntClosedRange(L_MIN, L_MAX); + int r = rndIntClosedRange(R_MIN, R_MAX); + int v = rndIntClosedRange(V_MIN, V_MAX); + if (l > r) { + int tmp = l; l = r; r = tmp; + } + out.print(' ');out.print(l);out.print(' ');out.print(r);out.print(' ');out.print(v);out.print('\n'); + } else if (type == 3) { // get + int p = rndIntClosedRange(P_MIN, P_MAX); + out.print(' '); out.print(p);out.print('\n'); + } else if (type == 4) { // prod + int l = rndIntClosedRange(L_MIN, L_MAX); + int r = rndIntClosedRange(R_MIN, R_MAX); + if (l > r) { + int tmp = l; l = r; r = tmp; + } + out.print(' ');out.print(l);out.print(' ');out.print(r);out.print('\n'); + } else if (type == 5) { // prodAll + out.print('\n'); + } else if (type == 6) { // maxRight + int l = rndIntClosedRange(L_MIN, L_MAX); + int v = rndIntClosedRange(V_MIN, V_MAX); + out.print(' ');out.print(l);out.print(' ');out.print(v);out.print('\n'); + } else if (type == 7) { // minLeft + int r = rndIntClosedRange(R_MIN, R_MAX); + int v = rndIntClosedRange(V_MIN, V_MAX); + out.print(' ');out.print(r);out.print(' ');out.print(v);out.print('\n'); + } else { + throw new AssertionError(); + } + } + } + + public static int rndIntClosedRange(int l, int r) { + return rnd.nextInt(r - l + 1) + l; + } +} diff --git a/LazySegTree/test/Main.java b/LazySegTree/test/Main.java new file mode 100644 index 0000000..217a425 --- /dev/null +++ b/LazySegTree/test/Main.java @@ -0,0 +1,14 @@ +package test; + +public class Main { + public static void main(String[] args) { + test(); + } + + static void test() { + Gen.main(null); + NaiveSolution.main(null); + Solution.main(null); + Test.main(null); + } +} diff --git a/LazySegTree/test/NaiveSolution.java b/LazySegTree/test/NaiveSolution.java new file mode 100644 index 0000000..fc31631 --- /dev/null +++ b/LazySegTree/test/NaiveSolution.java @@ -0,0 +1,93 @@ +package test; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Scanner; + +public class NaiveSolution { + static final int TEST_CASE_NUM = Gen.TESTCASE_NUM; + public static void main(String[] args) { + for (int i = 0; i < TEST_CASE_NUM; i++) { + String inputFileName = String.format("LazySegTree/test/in/testcase_%d", i); + String outputFileName = String.format("LazySegTree/test/answer/answer_%d", i); + try (Scanner sc = new Scanner(new File(inputFileName))) { + try (PrintWriter pw = new PrintWriter(new File(outputFileName))) { + solve(sc, pw); + sc.close(); + pw.flush(); + pw.close(); + } + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + static final long INF = 1l << 60; + + public static void solve(Scanner sc, PrintWriter pw) { + final int N = Integer.parseInt(sc.next()); + final int Q = Integer.parseInt(sc.next()); + final long[] A = new long[N]; + Arrays.setAll(A, i -> Integer.parseInt(sc.next())); + for (int i = 0; i < Q; i++) { + int queryType = Integer.parseInt(sc.next()); + if (queryType == 0) { + int p = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + A[p] = v; + } else if (queryType == 1) { + int p = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + A[p] += v; + } else if (queryType == 2) { + int l = Integer.parseInt(sc.next()); + int r = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + for (int j = l; j < r; j++) { + A[j] += v; + } + } else if (queryType == 3) { + int p = Integer.parseInt(sc.next()); + pw.println(A[p]); + } else if (queryType == 4) { + int l = Integer.parseInt(sc.next()); + int r = Integer.parseInt(sc.next()); + long min = INF; + for (int j = l; j < r; j++) { + min = Math.min(min, A[j]); + } + pw.println(min == INF ? "INF" : min); + } else if (queryType == 5) { + long min = INF; + for (int j = 0; j < N; j++) { + min = Math.min(min, A[j]); + } + pw.println(min); + } else if (queryType == 6) { + int l = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + for (int j = l; j <= N; j++) { + if (j == N || A[j] <= v) { + pw.println(j); + break; + } + } + } else if (queryType == 7) { + int r = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + for (int j = r; j >= 0; j--) { + if (j == 0 || A[j - 1] <= v) { + pw.println(j); + break; + } + } + } else { + throw new AssertionError(); + } + } + } +} diff --git a/LazySegTree/test/Readme.md b/LazySegTree/test/Readme.md new file mode 100644 index 0000000..0dc54e8 --- /dev/null +++ b/LazySegTree/test/Readme.md @@ -0,0 +1,41 @@ +# Test of Lazy Segment Tree + +遅延評価セグメント木のテストです. + +- `Gen.java` : テストケースを LazySegTree/test/in 内に生成します. +- `NaiveSolution.java` : LazySegTree/test/in 内のテストを愚直に解いて,解答を LazySegTree/test/answer 内に生成します. +- `Solution.java` : LazySegTree/test/in 内のテストを遅延評価セグメント木を用いて解いて,解答を LazySegTree/test/out 内に生成します. +- `Test.java` : LazySegTree/test/answer 内の解答と LazySegTree/test/out 内の解答が一致しているかをテストします. +- `Main.java` : `Gen.java`,`NaiveSolution.java`,`Solution.java`,`Test.java` を順に実行します. + +## テスト概要 + +整数列 $A_0, \dots, A_{N-1}$ が与えられる.以下に示すクエリが合計 $Q$ 個与えられるので,順に処理する. + +- `0 p v` : $a_p = v$ とする. +- `1 p v` : $a_p$ に $v$ を加算する. +- `2 l r v` : $a_l, \dots, a_{r-1}$ に $v$ を加算する. +- `3 p` : $a_p$ の値を求めて出力する. +- `4 l r` : $\min\{a_i\mid l\leq i \lt r\}$ を求めて出力する.但し,$l=r$ の場合は `INF` と出力する. +- `5` : $\min\{a_i\mid 0\leq i \lt N\}$ を求めて出力する. +- `6 l v` : $\max\{r\mid\min\{a_i\mid l\leq i \lt r\}\gt v\}$ を求めて出力する.但し,$a_l\leq v$ なら $l$ を出力する. +- `7 r v` : $\min\{l\mid\min\{a_i\mid l\leq i \lt r\}\gt v\}$ を求めて出力する.但し,$a_{r-1}\leq v$ なら $r$ を出力する. + +## 制約 + +- $1\leq N \leq 5000$ +- $1\leq Q \leq 10000$ +- $-10^9\leq A_i\leq 10^9$ +- $-10^9\leq v\leq 10^9$ +- $0\leq p\lt N$ +- $0\leq l\leq r\leq N$ + +## 入力 + +```math +N Q +A_{0} \dots A_{N-1} +Query_{0} +\vdots +Query_{Q-1} +``` diff --git a/LazySegTree/test/Solution.java b/LazySegTree/test/Solution.java new file mode 100644 index 0000000..2ebaf00 --- /dev/null +++ b/LazySegTree/test/Solution.java @@ -0,0 +1,376 @@ +package test; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Scanner; + +public class Solution { + static final int TEST_CASE_NUM = Gen.TESTCASE_NUM; + public static void main(String[] args) { + for (int i = 0; i < TEST_CASE_NUM; i++) { + String inputFileName = String.format("LazySegTree/test/in/testcase_%d", i); + String outputFileName = String.format("LazySegTree/test/out/out_%d", i); + try (Scanner sc = new Scanner(new File(inputFileName))) { + try (PrintWriter pw = new PrintWriter(new File(outputFileName))) { + solve(sc, pw); + sc.close(); + pw.flush(); + pw.close(); + } + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + static final long INF = 1l << 60; + + public static void solve(Scanner sc, PrintWriter pw) { + final int N = Integer.parseInt(sc.next()); + final int Q = Integer.parseInt(sc.next()); + final Long[] A = new Long[N]; + Arrays.setAll(A, i -> Long.parseLong(sc.next())); + LazySegTree t = new LazySegTree(A, Long::min, INF, (s, f) -> s + f, (s, f) -> s + f, 0l); + for (int i = 0; i < Q; i++) { + int queryType = Integer.parseInt(sc.next()); + if (queryType == 0) { + int p = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + t.set(p, (long) v); + } else if (queryType == 1) { + int p = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + t.apply(p, (long) v); + } else if (queryType == 2) { + int l = Integer.parseInt(sc.next()); + int r = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + t.apply(l, r, (long) v); + } else if (queryType == 3) { + int p = Integer.parseInt(sc.next()); + pw.println(t.get(p)); + } else if (queryType == 4) { + int l = Integer.parseInt(sc.next()); + int r = Integer.parseInt(sc.next()); + long min = t.prod(l, r); + pw.println(min == INF ? "INF" : min); + } else if (queryType == 5) { + pw.println(t.allProd()); + } else if (queryType == 6) { + int l = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + pw.println(t.maxRight(l, val -> val > v)); + } else if (queryType == 7) { + int r = Integer.parseInt(sc.next()); + int v = Integer.parseInt(sc.next()); + pw.println(t.minLeft(r, val -> val > v)); + } else { + throw new AssertionError(); + } + } + } +} + +/** + * TODO: verify {@link LazySegTree#maxRight} and {@link LazySegTree#minLeft} + * + * @verified https://atcoder.jp/contests/practice2/tasks/practice2_k + */ + +class LazySegTree { + final int MAX; + + final int N; + final int Log; + final java.util.function.BinaryOperator Op; + final S E; + final java.util.function.BiFunction Mapping; + final java.util.function.BinaryOperator Composition; + final F Id; + + final S[] Dat; + final F[] Laz; + + @SuppressWarnings("unchecked") + public LazySegTree(int n, java.util.function.BinaryOperator op, S e, java.util.function.BiFunction mapping, java.util.function.BinaryOperator composition, F id) { + this.MAX = n; + int k = 1; + while (k < n) k <<= 1; + this.N = k; + this.Log = Integer.numberOfTrailingZeros(N); + this.Op = op; + this.E = e; + this.Mapping = mapping; + this.Composition = composition; + this.Id = id; + this.Dat = (S[]) new Object[N << 1]; + this.Laz = (F[]) new Object[N]; + java.util.Arrays.fill(Dat, E); + java.util.Arrays.fill(Laz, Id); + } + + public LazySegTree(S[] dat, java.util.function.BinaryOperator op, S e, java.util.function.BiFunction mapping, java.util.function.BinaryOperator composition, F id) { + this(dat.length, op, e, mapping, composition, id); + build(dat); + } + + private void build(S[] dat) { + int l = dat.length; + System.arraycopy(dat, 0, Dat, N, l); + for (int i = N - 1; i > 0; i--) { + Dat[i] = Op.apply(Dat[i << 1 | 0], Dat[i << 1 | 1]); + } + } + + private void push(int k) { + if (Laz[k] == Id) return; + int lk = k << 1 | 0, rk = k << 1 | 1; + Dat[lk] = Mapping.apply(Laz[k], Dat[lk]); + Dat[rk] = Mapping.apply(Laz[k], Dat[rk]); + if (lk < N) Laz[lk] = Composition.apply(Laz[k], Laz[lk]); + if (rk < N) Laz[rk] = Composition.apply(Laz[k], Laz[rk]); + Laz[k] = Id; + } + + private void pushTo(int k) { + for (int i = Log; i > 0; i--) push(k >> i); + } + + private void pushTo(int lk, int rk) { + for (int i = Log; i > 0; i--) { + if (((lk >> i) << i) != lk) push(lk >> i); + if (((rk >> i) << i) != rk) push(rk >> i); + } + } + + private void updateFrom(int k) { + k >>= 1; + while (k > 0) { + Dat[k] = Op.apply(Dat[k << 1 | 0], Dat[k << 1 | 1]); + k >>= 1; + } + } + + private void updateFrom(int lk, int rk) { + for (int i = 1; i <= Log; i++) { + if (((lk >> i) << i) != lk) { + int lki = lk >> i; + Dat[lki] = Op.apply(Dat[lki << 1 | 0], Dat[lki << 1 | 1]); + } + if (((rk >> i) << i) != rk) { + int rki = (rk - 1) >> i; + Dat[rki] = Op.apply(Dat[rki << 1 | 0], Dat[rki << 1 | 1]); + } + } + } + + public void set(int p, S x) { + exclusiveRangeCheck(p); + p += N; + pushTo(p); + Dat[p] = x; + updateFrom(p); + } + + public S get(int p) { + exclusiveRangeCheck(p); + p += N; + pushTo(p); + return Dat[p]; + } + + public S prod(int l, int r) { + if (l > r) { + throw new IllegalArgumentException( + String.format("Invalid range: [%d, %d)", l, r) + ); + } + inclusiveRangeCheck(l); + inclusiveRangeCheck(r); + if (l == r) return E; + l += N; r += N; + pushTo(l, r); + S sumLeft = E, sumRight = E; + while (l < r) { + if ((l & 1) == 1) sumLeft = Op.apply(sumLeft, Dat[l++]); + if ((r & 1) == 1) sumRight = Op.apply(Dat[--r], sumRight); + l >>= 1; r >>= 1; + } + return Op.apply(sumLeft, sumRight); + } + + public S allProd() { + return Dat[1]; + } + + public void apply(int p, F f) { + exclusiveRangeCheck(p); + p += N; + pushTo(p); + Dat[p] = Mapping.apply(f, Dat[p]); + updateFrom(p); + } + + public void apply(int l, int r, F f) { + if (l > r) { + throw new IllegalArgumentException( + String.format("Invalid range: [%d, %d)", l, r) + ); + } + inclusiveRangeCheck(l); + inclusiveRangeCheck(r); + if (l == r) return; + l += N; r += N; + pushTo(l, r); + for (int l2 = l, r2 = r; l2 < r2;) { + if ((l2 & 1) == 1) { + Dat[l2] = Mapping.apply(f, Dat[l2]); + if (l2 < N) Laz[l2] = Composition.apply(f, Laz[l2]); + l2++; + } + if ((r2 & 1) == 1) { + r2--; + Dat[r2] = Mapping.apply(f, Dat[r2]); + if (r2 < N) Laz[r2] = Composition.apply(f, Laz[r2]); + } + l2 >>= 1; r2 >>= 1; + } + updateFrom(l, r); + } + + public int maxRight(int l, java.util.function.Predicate g) { + inclusiveRangeCheck(l); + if (!g.test(E)) { + throw new IllegalArgumentException("Identity element must satisfy the condition."); + } + if (l == MAX) return MAX; + l += N; + pushTo(l); + S sum = E; + do { + l >>= Integer.numberOfTrailingZeros(l); + if (!g.test(Op.apply(sum, Dat[l]))) { + while (l < N) { + push(l); + l = l << 1; + if (g.test(Op.apply(sum, Dat[l]))) { + sum = Op.apply(sum, Dat[l]); + l++; + } + } + return l - N; + } + sum = Op.apply(sum, Dat[l]); + l++; + } while ((l & -l) != l); + return MAX; + } + + public int minLeft(int r, java.util.function.Predicate g) { + inclusiveRangeCheck(r); + if (!g.test(E)) { + throw new IllegalArgumentException("Identity element must satisfy the condition."); + } + if (r == 0) return 0; + r += N; + pushTo(r - 1); + S sum = E; + do { + r--; + while (r > 1 && (r & 1) == 1) r >>= 1; + if (!g.test(Op.apply(Dat[r], sum))) { + while (r < N) { + push(r); + r = r << 1 | 1; + if (g.test(Op.apply(Dat[r], sum))) { + sum = Op.apply(Dat[r], sum); + r--; + } + } + return r + 1 - N; + } + sum = Op.apply(Dat[r], sum); + } while ((r & -r) != r); + return 0; + } + + private void exclusiveRangeCheck(int p) { + if (p < 0 || p >= MAX) { + throw new IndexOutOfBoundsException( + String.format("Index %d is not in [%d, %d).", p, 0, MAX) + ); + } + } + + private void inclusiveRangeCheck(int p) { + if (p < 0 || p > MAX) { + throw new IndexOutOfBoundsException( + String.format("Index %d is not in [%d, %d].", p, 0, MAX) + ); + } + } + + // **************** DEBUG **************** // + + private int indent = 6; + + public void setIndent(int newIndent) { + this.indent = newIndent; + } + + @Override + public String toString() { + return toSimpleString(); + } + + private S[] simulatePushAll() { + S[] simDat = java.util.Arrays.copyOf(Dat, 2 * N); + F[] simLaz = java.util.Arrays.copyOf(Laz, 2 * N); + for (int k = 1; k < N; k++) { + if (simLaz[k] == Id) continue; + int lk = k << 1 | 0, rk = k << 1 | 1; + simDat[lk] = Mapping.apply(simLaz[k], simDat[lk]); + simDat[rk] = Mapping.apply(simLaz[k], simDat[rk]); + if (lk < N) simLaz[lk] = Composition.apply(simLaz[k], simLaz[lk]); + if (rk < N) simLaz[rk] = Composition.apply(simLaz[k], simLaz[rk]); + simLaz[k] = Id; + } + return simDat; + } + + public String toDetailedString() { + return toDetailedString(1, 0, simulatePushAll()); + } + + private String toDetailedString(int k, int sp, S[] dat) { + if (k >= N) return indent(sp) + dat[k]; + String s = ""; + s += toDetailedString(k << 1 | 1, sp + indent, dat); + s += "\n"; + s += indent(sp) + dat[k]; + s += "\n"; + s += toDetailedString(k << 1 | 0, sp + indent, dat); + return s; + } + + private static String indent(int n) { + StringBuilder sb = new StringBuilder(); + while (n --> 0) sb.append(' '); + return sb.toString(); + } + + public String toSimpleString() { + S[] dat = simulatePushAll(); + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0; i < N; i++) { + sb.append(dat[i + N]); + if (i < N - 1) sb.append(',').append(' '); + } + sb.append(']'); + return sb.toString(); + } +} \ No newline at end of file diff --git a/LazySegTree/test/Test.java b/LazySegTree/test/Test.java new file mode 100644 index 0000000..44a1db1 --- /dev/null +++ b/LazySegTree/test/Test.java @@ -0,0 +1,41 @@ +package test; + +import java.io.File; +import java.io.IOException; +import java.util.Scanner; + +public class Test { + static final int TEST_CASE_NUM = Gen.TESTCASE_NUM; + + public static void main(String[] args) { + for (int i = 0; i < TEST_CASE_NUM; i++) { + String answerFileName = String.format("LazySegTree/test/answer/answer_%d", i); + String outputFileName = String.format("LazySegTree/test/out/out_%d", i); + try (Scanner scAns = new Scanner(new File(answerFileName))) { + try (Scanner scSlv = new Scanner(new File(outputFileName))) { + test(scAns, scSlv); + } catch (AssertionError e) { + System.err.printf("Error in test %d", i); + e.printStackTrace(); + } + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + public static void test(Scanner scAns, Scanner scSlv) { + int line = 1; + while (scAns.hasNext() && scSlv.hasNext()) { + String ans = scAns.next(); + String slv = scSlv.next(); + if (!ans.equals(slv)) { + throw new AssertionError( + String.format("Filed at line %d.\n\texpected: %s\n\tactual: %s", line, ans, slv) + ); + } + line++; + } + } +} diff --git a/Math/MathLib.java b/Math/MathLib.java index 73af073..c85cf34 100644 --- a/Math/MathLib.java +++ b/Math/MathLib.java @@ -22,13 +22,37 @@ private static long[] inv_gcd(long a, long b){ return new long[]{s,m0}; } - public static long pow_mod(long x, long n, long m){ - assert(n >= 0 && m >= 1); - long ans = 1; + public static long gcd(long... a){ + if(a.length == 0) return 0; + long r = java.lang.Math.abs(a[0]); + for(int i=1; i= 0; + assert m >= 1; + if(m == 1)return 0L; + x = safe_mod(x, m); + long ans = 1L; while(n > 0){ - if(n%2==1) ans = (ans * x) % m; + if((n&1) == 1) ans = (ans * x) % m; x = (x*x) % m; - n /= 2; + n >>>= 1; } return ans; } @@ -83,4 +107,18 @@ public static long floor_sum(long n, long m, long a, long b){ ans += floor_sum(y_max, a, m, (a-x_max%a)%a); return ans; } + + public static java.util.ArrayList divisors(long n){ + java.util.ArrayList divisors = new ArrayList<>(); + java.util.ArrayList large = new ArrayList<>(); + + for(long i=1; i*i<=n; i++) if(n%i==0){ + divisors.add(i); + if(i*i=0; p--){ + divisors.add(large.get(p)); + } + return divisors; + } } \ No newline at end of file diff --git a/Math/README.md b/Math/README.md index f2970b3..b227b3e 100644 --- a/Math/README.md +++ b/Math/README.md @@ -21,4 +21,18 @@ $$ x \equiv r[i] \mod m[i] $$ を解きます. public static long floor_sum(long n, long m, long a, long b) ``` -$ \sigma_{i=0}^{n-1} floor(\frac{a*i+b}{m}) $を返します. \ No newline at end of file +$ \sigma_{i=0}^{n-1} floor(\frac{a*i+b}{m}) $を返します. + +## gcd, lcm + +```java +public static long gcd(long a, long b) +public static long lcm(long a, long b) +``` +二整数 a, b の最大公約数/最小公倍数を返します. 返り値は必ず非負整数になります. + +```java +public static java.util.ArrayList divisors(int n) +``` +整数nの約数を昇順に含んだリストを返します. +計算量: O(√n) \ No newline at end of file diff --git a/MaxFlow/Main.java b/MaxFlow/Main.java new file mode 100644 index 0000000..5325bc0 --- /dev/null +++ b/MaxFlow/Main.java @@ -0,0 +1,253 @@ +import java.util.Arrays; +import java.util.Scanner; + +public class Main { + static Solver practice2_d = new practice2_d(); + static Solver arc074_d = new arc074_d(); + static Solver abc193_f = new abc193_f(); + + public static void main(String[] args) { + // practice2_d.run(); + // arc074_d.run(); + abc193_f.run(); + } +} + +abstract class Solver implements Runnable { public abstract void run(); } + +/** + * @problem + * AtCoder Library Practice Contest D - Maxflow + * {@see https://atcoder.jp/contests/practice2/tasks/practice2_d} + * @submission + * {@see https://atcoder.jp/contests/practice2/submissions/20808482} + */ +class practice2_d extends Solver { + public Answer solve(int n, int m, char[][] g) { + MaxFlow mf = new MaxFlow(n * m + 2); + int s = n * m; + int t = s + 1; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (g[i][j] == '#') continue; + if (((i ^ j) & 1) == 0) { + mf.addEdge(s, i * m + j, 1); + } else { + mf.addEdge(i * m + j, t, 1); + } + } + } + for (int i = 0; i < n; i++) { + for (int j = i & 1; j < m; j += 2) { + if (g[i][j] == '#') continue; + if (j - 1 >= 0 && g[i][j - 1] == '.') { + mf.addEdge(i * m + j, i * m + (j - 1), 1); + } + if (j + 1 < m && g[i][j + 1] == '.') { + mf.addEdge(i * m + j, i * m + (j + 1), 1); + } + if (i - 1 >= 0 && g[i - 1][j] == '.') { + mf.addEdge(i * m + j, (i - 1) * m + j, 1); + } + if (i + 1 < n && g[i + 1][j] == '.') { + mf.addEdge(i * m + j, (i + 1) * m + j, 1); + } + } + } + int ans = (int) mf.maxFlow(s, t); + int cnt = 0; + for (var e : mf.getEdges()) { + if (e.flow == 0) continue; + int u = e.from; + int v = e.to; + if (u == s || v == t) continue; + int ui = u / m, uj = u % m; + int vi = v / m, vj = v % m; + if (g[ui][uj] != '.' || g[vi][vj] != '.') { + throw new AssertionError(); + } + if (ui == vi) { + if (uj + 1 == vj) { + g[ui][uj] = '>'; + g[vi][vj] = '<'; + } else if (uj == vj + 1) { + g[ui][uj] = '<'; + g[vi][vj] = '>'; + } else { + throw new AssertionError(); + } + } else if (uj == vj) { + if (ui + 1 == vi) { + g[ui][uj] = 'v'; + g[vi][vj] = '^'; + } else if (ui == vi + 1) { + g[ui][uj] = '^'; + g[vi][vj] = 'v'; + } else { + throw new AssertionError(); + } + } else { + throw new AssertionError(); + } + cnt++; + } + if (ans != cnt) throw new AssertionError(); + return new Answer(ans, g); + } + + static class Answer { + final int c; + final char[][] g; + Answer(int c, char[][] g) { this.c = c; this.g = g; } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(c); + for (char[] row : g) { + sb.append('\n').append(row); + } + return sb.toString(); + } + } + + public void run() { + Scanner sc = new Scanner(System.in); + int n = Integer.parseInt(sc.next()); + int m = Integer.parseInt(sc.next()); + char[][] g = new char[n][]; + Arrays.setAll(g, i -> sc.next().toCharArray()); + sc.close(); + System.out.println(solve(n, m, g)); + } +} + +/** + * @problem + * AtCoder Regular Contest 074 F - Lotus Leaves + * {@see https://atcoder.jp/contests/arc074/tasks/arc074_d} + * @submission + * {@see https://atcoder.jp/contests/arc074/submissions/20808863} + */ +class arc074_d extends Solver { + static final int INF = 1 << 20; + public int solve(int h, int w, char[][] g) { + int si = -1, sj = -1, ti = -1, tj = -1; + boolean[][] grid = new boolean[h][w]; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + if (g[i][j] == 'S') { + si = i; sj = j; + grid[i][j] = true; + } else if (g[i][j] == 'T') { + ti = i; tj = j; + grid[i][j] = true; + } else if (g[i][j] == 'o') { + grid[i][j] = true; + } + } + } + int s = h + w; + int t = s + 1; + MaxFlow mf = new MaxFlow(h + w + 2); + mf.addEdge(s , si , INF); + mf.addEdge(s , sj + h, INF); + mf.addEdge(ti , t , INF); + mf.addEdge(tj + h, t , INF); + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + if (grid[i][j]) { + mf.addEdge(i, j + h, 1); + mf.addEdge(j + h, i, 1); + } + } + } + int flow = (int) mf.maxFlow(s, t); + return flow >= INF ? -1 : flow; + } + + public void run() { + Scanner sc = new Scanner(System.in); + int h = Integer.parseInt(sc.next()); + int w = Integer.parseInt(sc.next()); + char[][] g = new char[h][]; + Arrays.setAll(g, i -> sc.next().toCharArray()); + sc.close(); + System.out.println(solve(h, w, g)); + } +} + +/** + * @problem + * AtCoder Beginner Contest 193 F - Zebraness + * {@see https://atcoder.jp/contests/abc193/tasks/abc193_f} + * @submission + * {@see https://atcoder.jp/contests/abc193/submissions/20808965} + */ +class abc193_f extends Solver { + static final int[] dx4 = {1, 0, -1, 0}; + static final int[] dy4 = {0, 1, 0, -1}; + + public int solve(int n, char[][] c) { + int[][] id = new int[n][n]; + int cnt = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (c[i][j] == '?') { + id[i][j] = cnt++; + } + } + } + + int ans = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i < n - 1) { + if (c[i][j] == 'B' && c[i + 1][j] == 'W' || c[i][j] == 'W' && c[i + 1][j] == 'B') { + ans++; + } + } + if (j < n - 1) { + if (c[i][j] == 'B' && c[i][j + 1] == 'W' || c[i][j] == 'W' && c[i][j + 1] == 'B') { + ans++; + } + } + } + } + MaxFlow mf = new MaxFlow(cnt + 2); + int s = cnt, t = cnt + 1; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (c[i][j] == '?') { + int b = 0, w = 0; + for (int d = 0; d < 4; d++) { + int ni = i + dx4[d]; + int nj = j + dy4[d]; + if (ni < 0 || ni >= n || nj < 0 || nj >= n) continue; + if (c[ni][nj] == 'B') { + b++; + } else if (c[ni][nj] == 'W') { + w++; + } else if (d < 2) { + mf.addEdge(id[i][j], id[ni][nj], 1); + mf.addEdge(id[ni][nj], id[i][j], 1); + ans++; + } + } + mf.addEdge(id[i][j], t, (i + j) % 2 == 1 ? b : w); + mf.addEdge(s, id[i][j], (i + j) % 2 == 1 ? w : b); + ans += b + w; + } + } + } + return (int) (ans - mf.maxFlow(s, t)); + } + + public void run() { + Scanner sc = new Scanner(System.in); + int n = Integer.parseInt(sc.next()); + char[][] c = new char[n][]; + Arrays.setAll(c, i -> sc.next().toCharArray()); + sc.close(); + System.out.println(solve(n, c)); + } +} diff --git a/MaxFlow/MaxFlow.java b/MaxFlow/MaxFlow.java index 61a2548..591fc2f 100644 --- a/MaxFlow/MaxFlow.java +++ b/MaxFlow/MaxFlow.java @@ -1,80 +1,93 @@ -/** - * @verified https://atcoder.jp/contests/practice2/tasks/practice2_d - */ class MaxFlow { - public class CapEdge { - private final int from, to; - private long cap; - private final int rev; - CapEdge(int from, int to, long cap, int rev) { - this.from = from; - this.to = to; - this.cap = cap; - this.rev = rev; + private static final class InternalCapEdge { + final int to; + final int rev; + long cap; + InternalCapEdge(int to, int rev, long cap) { this.to = to; this.rev = rev; this.cap = cap; } + } + public static final class CapEdge { + public final int from, to; + public final long cap, flow; + CapEdge(int from, int to, long cap, long flow) { this.from = from; this.to = to; this.cap = cap; this.flow = flow; } + @Override + public boolean equals(Object o) { + if (o instanceof CapEdge) { + CapEdge e = (CapEdge) o; + return from == e.from && to == e.to && cap == e.cap && flow == e.flow; + } + return false; } - public int getFrom() {return from;} - public int getTo() {return to;} - public long getCap() {return cap;} - public long getFlow() {return g[to][rev].cap;} + } + private static final class IntPair { + final int first, second; + IntPair(int first, int second) { this.first = first; this.second = second; } } - private static final long INF = Long.MAX_VALUE; + static final long INF = Long.MAX_VALUE; private final int n; - private int m; - private final java.util.ArrayList edges; - private final int[] count; - private final CapEdge[][] g; + private final java.util.ArrayList pos; + private final java.util.ArrayList[] g; + @SuppressWarnings("unchecked") public MaxFlow(int n) { this.n = n; - this.edges = new java.util.ArrayList<>(); - this.count = new int[n]; - this.g = new CapEdge[n][]; + this.pos = new java.util.ArrayList<>(); + this.g = new java.util.ArrayList[n]; + for (int i = 0; i < n; i++) { + this.g[i] = new java.util.ArrayList<>(); + } } public int addEdge(int from, int to, long cap) { rangeCheck(from, 0, n); rangeCheck(to, 0, n); nonNegativeCheck(cap, "Capacity"); - CapEdge e = new CapEdge(from, to, cap, count[to]); - count[from]++; count[to]++; - edges.add(e); - return m++; + int m = pos.size(); + pos.add(new IntPair(from, g[from].size())); + int fromId = g[from].size(); + int toId = g[to].size(); + if (from == to) toId++; + g[from].add(new InternalCapEdge(to, toId, cap)); + g[to].add(new InternalCapEdge(from, fromId, 0L)); + return m; + } + + private InternalCapEdge getInternalEdge(int i) { + return g[pos.get(i).first].get(pos.get(i).second); + } + + private InternalCapEdge getInternalEdgeReversed(InternalCapEdge e) { + return g[e.to].get(e.rev); } public CapEdge getEdge(int i) { + int m = pos.size(); rangeCheck(i, 0, m); - return edges.get(i); + InternalCapEdge e = getInternalEdge(i); + InternalCapEdge re = getInternalEdgeReversed(e); + return new CapEdge(re.to, e.to, e.cap + re.cap, re.cap); } - public java.util.ArrayList getEdges() { - return edges; + public CapEdge[] getEdges() { + CapEdge[] res = new CapEdge[pos.size()]; + java.util.Arrays.setAll(res, this::getEdge); + return res; } public void changeEdge(int i, long newCap, long newFlow) { + int m = pos.size(); rangeCheck(i, 0, m); nonNegativeCheck(newCap, "Capacity"); if (newFlow > newCap) { throw new IllegalArgumentException( - String.format("Flow %d is greater than capacity %d.", newCap, newFlow) + String.format("Flow %d is greater than the capacity %d.", newCap, newFlow) ); } - CapEdge e = edges.get(i); - CapEdge er = g[e.to][e.rev]; + InternalCapEdge e = getInternalEdge(i); + InternalCapEdge re = getInternalEdgeReversed(e); e.cap = newCap - newFlow; - er.cap = newFlow; - } - - private void buildGraph() { - for (int i = 0; i < n; i++) { - g[i] = new CapEdge[count[i]]; - } - int[] idx = new int[n]; - for (CapEdge e : edges) { - g[e.to][idx[e.to]++] = new CapEdge(e.to, e.from, 0, idx[e.from]); - g[e.from][idx[e.from]++] = e; - } + re.cap = newFlow; } public long maxFlow(int s, int t) { @@ -84,33 +97,33 @@ public long maxFlow(int s, int t) { public long flow(int s, int t, long flowLimit) { rangeCheck(s, 0, n); rangeCheck(t, 0, n); - buildGraph(); - long flow = 0; + long flow = 0L; int[] level = new int[n]; int[] que = new int[n]; int[] iter = new int[n]; - while (true) { - java.util.Arrays.fill(level, -1); - dinicBFS(s, t, level, que); - if (level[t] < 0) return flow; + while (flow < flowLimit) { + bfs(s, t, level, que); + if (level[t] < 0) break; java.util.Arrays.fill(iter, 0); - while (true) { - long d = dinicDFS(t, s, flowLimit - flow, iter, level); - if (d <= 0) break; + while (flow < flowLimit) { + long d = dfs(t, s, flowLimit - flow, iter, level); + if (d == 0) break; flow += d; } } + return flow; } - private void dinicBFS(int s, int t, int[] level, int[] que) { + private void bfs(int s, int t, int[] level, int[] que) { + java.util.Arrays.fill(level, -1); int hd = 0, tl = 0; que[tl++] = s; level[s] = 0; - while (tl > hd) { + while (hd < tl) { int u = que[hd++]; - for (CapEdge e : g[u]) { + for (InternalCapEdge e : g[u]) { int v = e.to; - if (e.cap <= 0 || level[v] >= 0) continue; + if (e.cap == 0 || level[v] >= 0) continue; level[v] = level[u] + 1; if (v == t) return; que[tl++] = v; @@ -118,76 +131,46 @@ private void dinicBFS(int s, int t, int[] level, int[] que) { } } - private long dinicDFS(int cur, int s, long f, int[] iter, int[] level) { - if (cur == s) return f; + private long dfs(int cur, int s, long flowLimit, int[] iter, int[] level) { + if (cur == s) return flowLimit; long res = 0; - while (iter[cur] < count[cur]) { - CapEdge er = g[cur][iter[cur]++]; - int u = er.to; - CapEdge e = g[u][er.rev]; - if (level[u] >= level[cur] || e.cap <= 0) continue; - long d = dinicDFS(u, s, Math.min(f - res, e.cap), iter, level); + int curLevel = level[cur]; + for (int itMax = g[cur].size(); iter[cur] < itMax; iter[cur]++) { + int i = iter[cur]; + InternalCapEdge e = g[cur].get(i); + InternalCapEdge re = getInternalEdgeReversed(e); + if (curLevel <= level[e.to] || re.cap == 0) continue; + long d = dfs(e.to, s, Math.min(flowLimit - res, re.cap), iter, level); if (d <= 0) continue; - e.cap -= d; - er.cap += d; + e.cap += d; + re.cap -= d; res += d; - if (res == f) break; + if (res == flowLimit) break; } return res; } - public long fordFulkersonMaxFlow(int s, int t) { - return fordFulkersonFlow(s, t, INF); - } - - public long fordFulkersonFlow(int s, int t, long flowLimit) { - rangeCheck(s, 0, n); - rangeCheck(t, 0, n); - buildGraph(); - boolean[] used = new boolean[n]; - long flow = 0; - while (true) { - java.util.Arrays.fill(used, false); - long f = fordFulkersonDFS(s, t, flowLimit - flow, used); - if (f <= 0) return flow; - flow += f; - } - } - - private long fordFulkersonDFS(int cur, int t, long f, boolean[] used) { - if (cur == t) return f; - used[cur] = true; - for (CapEdge e : g[cur]) { - if (used[e.to] || e.cap <= 0) continue; - long d = fordFulkersonDFS(e.to, t, Math.min(f, e.cap), used); - if (d <= 0) continue; - e.cap -= d; - g[e.to][e.rev].cap += d; - return d; - } - return 0; - } - public boolean[] minCut(int s) { rangeCheck(s, 0, n); - boolean[] reachable = new boolean[n]; + boolean[] visited = new boolean[n]; int[] stack = new int[n]; int ptr = 0; stack[ptr++] = s; - reachable[s] = true; + visited[s] = true; while (ptr > 0) { int u = stack[--ptr]; - for (CapEdge e : g[u]) { + for (InternalCapEdge e : g[u]) { int v = e.to; - if (reachable[v] || e.cap <= 0) continue; - reachable[v] = true; - stack[ptr++] = v; + if (e.cap > 0 && !visited[v]) { + visited[v] = true; + stack[ptr++] = v; + } } } - return reachable; + return visited; } - private void rangeCheck(int i, int minInlusive, int maxExclusive) { + private void rangeCheck(int i, int minInclusive, int maxExclusive) { if (i < 0 || i >= maxExclusive) { throw new IndexOutOfBoundsException( String.format("Index %d out of bounds for length %d", i, maxExclusive) @@ -195,11 +178,11 @@ private void rangeCheck(int i, int minInlusive, int maxExclusive) { } } - private void nonNegativeCheck(long cap, java.lang.String attribute) { + private void nonNegativeCheck(long cap, String attribute) { if (cap < 0) { throw new IllegalArgumentException( String.format("%s %d is negative.", attribute, cap) ); } } -} \ No newline at end of file +} diff --git a/MaxFlow/MaxFlowTest.java b/MaxFlow/MaxFlowTest.java new file mode 100644 index 0000000..58d578c --- /dev/null +++ b/MaxFlow/MaxFlowTest.java @@ -0,0 +1,150 @@ +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; + +/** + * From: https://github.com/atcoder/ac-library/blob/1ca9100261b8c27cf62acccc3618c5e8375bf57b/test/unittest/maxflow_test.cpp + */ +public class MaxFlowTest { + @Test + public void simple() { + MaxFlow g = new MaxFlow(4); + assertEquals(0, g.addEdge(0, 1, 1)); + assertEquals(1, g.addEdge(0, 2, 1)); + assertEquals(2, g.addEdge(1, 3, 1)); + assertEquals(3, g.addEdge(2, 3, 1)); + assertEquals(4, g.addEdge(1, 2, 1)); + assertEquals(2, g.maxFlow(0, 3)); + + MaxFlow.CapEdge e; + e = new MaxFlow.CapEdge(0, 1, 1, 1); + assertEquals(e, g.getEdge(0)); + e = new MaxFlow.CapEdge(0, 2, 1, 1); + assertEquals(e, g.getEdge(1)); + e = new MaxFlow.CapEdge(1, 3, 1, 1); + assertEquals(e, g.getEdge(2)); + e = new MaxFlow.CapEdge(2, 3, 1, 1); + assertEquals(e, g.getEdge(3)); + e = new MaxFlow.CapEdge(1, 2, 1, 0); + assertEquals(e, g.getEdge(4)); + + assertTrue(Arrays.equals(new boolean[]{true, false, false, false}, g.minCut(0))); + } + + @Test + public void notSimple() { + MaxFlow g = new MaxFlow(2); + assertEquals(0, g.addEdge(0, 1, 1)); + assertEquals(1, g.addEdge(0, 1, 2)); + assertEquals(2, g.addEdge(0, 1, 3)); + assertEquals(3, g.addEdge(0, 1, 4)); + assertEquals(4, g.addEdge(0, 1, 5)); + assertEquals(5, g.addEdge(0, 0, 6)); + assertEquals(6, g.addEdge(1, 1, 7)); + assertEquals(15, g.maxFlow(0, 1)); + + MaxFlow.CapEdge e; + e = new MaxFlow.CapEdge(0, 1, 1, 1); + assertEquals(e, g.getEdge(0)); + e = new MaxFlow.CapEdge(0, 1, 2, 2); + assertEquals(e, g.getEdge(1)); + e = new MaxFlow.CapEdge(0, 1, 3, 3); + assertEquals(e, g.getEdge(2)); + e = new MaxFlow.CapEdge(0, 1, 4, 4); + assertEquals(e, g.getEdge(3)); + e = new MaxFlow.CapEdge(0, 1, 5, 5); + assertEquals(e, g.getEdge(4)); + + assertTrue(Arrays.equals(new boolean[]{true, false}, g.minCut(0))); + } + + @Test + public void cut() { + MaxFlow g = new MaxFlow(3); + assertEquals(0, g.addEdge(0, 1, 2)); + assertEquals(1, g.addEdge(1, 2, 1)); + assertEquals(1, g.maxFlow(0, 2)); + + MaxFlow.CapEdge e; + e = new MaxFlow.CapEdge(0, 1, 2, 1); + assertEquals(e, g.getEdge(0)); + e = new MaxFlow.CapEdge(1, 2, 1, 1); + assertEquals(e, g.getEdge(1)); + + assertTrue(Arrays.equals(new boolean[]{true, true, false}, g.minCut(0))); + } + + @Test + public void twise() { + MaxFlow.CapEdge e; + + MaxFlow g = new MaxFlow(3); + assertEquals(0, g.addEdge(0, 1, 1)); + assertEquals(1, g.addEdge(0, 2, 1)); + assertEquals(2, g.addEdge(1, 2, 1)); + + assertEquals(2, g.maxFlow(0, 2)); + + e = new MaxFlow.CapEdge(0, 1, 1, 1); + assertEquals(e, g.getEdge(0)); + e = new MaxFlow.CapEdge(0, 2, 1, 1); + assertEquals(e, g.getEdge(1)); + e = new MaxFlow.CapEdge(1, 2, 1, 1); + assertEquals(e, g.getEdge(2)); + + g.changeEdge(0, 100, 10); + e = new MaxFlow.CapEdge(0, 1, 100, 10); + assertEquals(e, g.getEdge(0)); + + assertEquals(0, g.maxFlow(0, 2)); + assertEquals(90, g.maxFlow(0, 1)); + + e = new MaxFlow.CapEdge(0, 1, 100, 100); + assertEquals(e, g.getEdge(0)); + e = new MaxFlow.CapEdge(0, 2, 1, 1); + assertEquals(e, g.getEdge(1)); + e = new MaxFlow.CapEdge(1, 2, 1, 1); + assertEquals(e, g.getEdge(2)); + + assertEquals(2, g.maxFlow(2, 0)); + + e = new MaxFlow.CapEdge(0, 1, 100, 99); + assertEquals(e, g.getEdge(0)); + e = new MaxFlow.CapEdge(0, 2, 1, 0); + assertEquals(e, g.getEdge(1)); + e = new MaxFlow.CapEdge(1, 2, 1, 0); + assertEquals(e, g.getEdge(2)); + } + + @Test + public void bound() { + MaxFlow.CapEdge e; + + long INF = MaxFlow.INF; + MaxFlow g = new MaxFlow(3); + assertEquals(0, g.addEdge(0, 1, INF)); + assertEquals(1, g.addEdge(1, 0, INF)); + assertEquals(2, g.addEdge(0, 2, INF)); + + assertEquals(INF, g.maxFlow(0, 2)); + + e = new MaxFlow.CapEdge(0, 1, INF, 0); + assertEquals(e, g.getEdge(0)); + e = new MaxFlow.CapEdge(1, 0, INF, 0); + assertEquals(e, g.getEdge(1)); + e = new MaxFlow.CapEdge(0, 2, INF, INF); + assertEquals(e, g.getEdge(2)); + } + + @Test + public void selfLoop() { + MaxFlow g = new MaxFlow(3); + assertEquals(0, g.addEdge(0, 0, 100)); + + MaxFlow.CapEdge e = new MaxFlow.CapEdge(0, 0, 100, 0); + assertEquals(e, g.getEdge(0)); + } +} \ No newline at end of file diff --git a/MaxFlow/Readme.md b/MaxFlow/Readme.md index 2e2df9d..a4781d3 100644 --- a/MaxFlow/Readme.md +++ b/MaxFlow/Readme.md @@ -38,16 +38,10 @@ public void addEdge(int from, int to, long cap) public long maxFlow(int s, int t) // (2) public long flow(int s, int t, long flowLimit) -// (3) -public long fordFulkersonMaxFlow(int s, int t) -// (4) -public long fordFulkersonFlow(int s, int t, long flowLimit) ``` - (1): 頂点 `s` から `t` へ流せる限り流し、流せた量を返す。Dinic のアルゴルズムを用います。 - (2) 頂点 `s` から `t` へ流量 `flowLimit` に達するまで流せる限り流し、流せた量を返す。Dinic のアルゴルズムを用います。 -- (3) 頂点 `s` から `t` へ流せる限り流し、流せた量を返す。FordFulkerson のアルゴルズムを用います。 -- (4) 頂点 `s` から `t` へ流量 `flowLimit` に達するまで流せる限り流し、流せた量を返す。FordFulkerson のアルゴルズムを用います。 - 複数回呼んだ場合は,前回流したフローからの差分が返ります。 制約 @@ -60,7 +54,6 @@ public long fordFulkersonFlow(int s, int t, long flowLimit) - (1), (2) $O(\min(n^{2/3}*m, m^{3/2}))$ (辺の容量がすべて 1 の時) - (1), (2) $O(n^2*m)$ -- (3), (4) 返り値を `f` として,`O(f*m)` ### minCut @@ -76,34 +69,34 @@ public boolean[] minCut(int s) ### getEdge / getEdges -`getEdge`、`getEdges` の返り値には以下のメソッドを持つ `MaxFlow.CapEdge` 型が用いられています。 +`getEdge`、`getEdges` の返り値には以下のメンバを持つ `MaxFlow.CapEdge` 型が用いられています。 ```java class CapEdge { // (1) - public int getFrom() + public final int from; // (2) - public int getTo() + public final int to; // (3) - public long getCap() + public final long cap; // (4) - public long getFlow() + public final long flow; }; ``` -- (1): 始点を返します。計算量: $O(1)$ -- (2): 終点を返します。計算量: $O(1)$ -- (3): 現在の容量を返します。計算量: $O(1)$ +- (1): 辺の始点を返します。計算量: $O(1)$ +- (2): 辺の終点を返します。計算量: $O(1)$ +- (3): 辺の容量を返します。計算量: $O(1)$ - (4): 現在の流量を返しまず。計算量: $O(1)$ ```java // (1) public CapEdge getEdge(int i) // (2) -public java.util.ArrayList getEdges() +public CapEdge[] getEdges() ``` -今の内部の辺の状態を返す。辺の順番は `addEdge` で追加された順番と同一。 +現在の内部の辺の状態を返す。辺の順番は `addEdge` で追加された順番と同一。 制約 @@ -112,7 +105,7 @@ public java.util.ArrayList getEdges() 計算量 - (1): $O(1)$ -- (2): $O(1)$ +- (2): $O(m)$ ### changeEdge @@ -132,4 +125,4 @@ $O(1) ## 使用例 -[AtCoder Library Practice Contest D - Maxflow](https://atcoder.jp/contests/practice2/submissions/16646346) +[AtCoder Library Practice Contest D - Maxflow](https://atcoder.jp/contests/practice2/submissions/20808482) diff --git a/MinCostFlow/Main.java b/MinCostFlow/Main.java new file mode 100644 index 0000000..7b313ec --- /dev/null +++ b/MinCostFlow/Main.java @@ -0,0 +1,116 @@ +import java.util.Arrays; +import java.util.Scanner; + +public class Main { + static Solver practice2_e = new practice2_e(); + static Solver GRL_6_B = new GRL_6_B(); + + public static void main(String[] args) { + // practice2_e.run(); + GRL_6_B.run(); + } +} + +abstract class Solver implements Runnable { public abstract void run(); } + +/** + * @problem + * AtCoder Library Practice Contest E - MinCostFlow + * {@see https://atcoder.jp/contests/practice2/tasks/practice2_e} + * @submission + * {@see https://atcoder.jp/contests/practice2/submissions/20811599} + */ +class practice2_e extends Solver { + static class Answer { + final long ans; + final char[][] g; + Answer(long ans, char[][] g) { + this.ans = ans; + this.g = g; + } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(ans); + for (char[] r : g) { + sb.append('\n').append(r); + } + return sb.toString(); + } + } + public void run() { + Scanner sc = new Scanner(System.in); + int n = Integer.parseInt(sc.next()); + int k = Integer.parseInt(sc.next()); + long[][] a = new long[n][n]; + for (long[] r : a) Arrays.setAll(r, j -> Long.parseLong(sc.next())); + sc.close(); + System.out.println(solve(n, k, a)); + } + + static final long INF = 1l << 50; + + public Answer solve(int n, int k, long[][] a) { + MinCostFlow mcf = new MinCostFlow(n + n + 2); + int s = n + n , t = s + 1; + mcf.addEdge(s, t, n * k, INF); + + for(int i = 0; i < n; i++) { + mcf.addEdge(s, i, k, 0); + mcf.addEdge(i + n, t, k, 0); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + mcf.addEdge(i, j + n, 1, INF - a[i][j]); + } + } + + MinCostFlow.FlowAndCost res = mcf.minCostFlow(s, t, n * k); + long ans = INF * res.flow - res.cost; + MinCostFlow.WeightedCapEdge[] es = mcf.getEdges(); + char[][] g = new char[n][n]; + for (char[] r : g) Arrays.fill(r, '.'); + for (MinCostFlow.WeightedCapEdge e : es) { + if (e.flow == 1 && e.from != s && e.to != t) { + g[e.from][e.to - n] = 'X'; + } + } + return new Answer(ans, g); + } +} + +/** + * @problem + * GRL_6 B - Minimum Cost Flow + * {@see https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B} + * @submission + * {@see https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=5281698#1} + */ +class GRL_6_B extends Solver { + public int solve(int n, int m, int f, int[][] edges) { + MinCostFlow mcf = new MinCostFlow(n); + for (int[] edge : edges) { + int u = edge[0]; + int v = edge[1]; + int c = edge[2]; + int d = edge[3]; + mcf.addEdge(u, v, c, d); + } + MinCostFlow.FlowAndCost fc = mcf.minCostFlow(0, n - 1, f); + return (int) (fc.flow == f ? fc.cost : -1); + } + + public void run() { + Scanner sc = new Scanner(System.in); + int v = Integer.parseInt(sc.next()); + int e = Integer.parseInt(sc.next()); + int f = Integer.parseInt(sc.next()); + int[][] edges = new int[e][4]; + for (int[] edge : edges) { + Arrays.setAll(edge, i -> Integer.parseInt(sc.next())); + } + sc.close(); + System.out.println(solve(v, e, f, edges)); + } +} diff --git a/MinCostFlow/MinCostFlow.java b/MinCostFlow/MinCostFlow.java index 8b57954..91689a7 100644 --- a/MinCostFlow/MinCostFlow.java +++ b/MinCostFlow/MinCostFlow.java @@ -1,48 +1,62 @@ /** - * @verified + * @verified * - https://atcoder.jp/contests/practice2/tasks/practice2_e * - http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B */ class MinCostFlow { - public class WeightedCapEdge { - private final int from, to; - private long cap; - private long cost; - private final int rev; - WeightedCapEdge(int from, int to, long cap, long cost, int rev) { - this.from = from; - this.to = to; - this.cap = cap; - this.cost = cost; - this.rev = rev; + private static final class InternalWeightedCapEdge { + final int to, rev; + long cap; + final long cost; + InternalWeightedCapEdge(int to, int rev, long cap, long cost) { this.to = to; this.rev = rev; this.cap = cap; this.cost = cost; } + } + + public static final class WeightedCapEdge { + public final int from, to; + public final long cap, flow, cost; + WeightedCapEdge(int from, int to, long cap, long flow, long cost) { this.from = from; this.to = to; this.cap = cap; this.flow = flow; this.cost = cost; } + @Override + public boolean equals(Object o) { + if (o instanceof WeightedCapEdge) { + WeightedCapEdge e = (WeightedCapEdge) o; + return from == e.from && to == e.to && cap == e.cap && flow == e.flow && cost == e.cost; + } + return false; } - public int getFrom() {return from;} - public int getTo() {return to;} - public long getCap() {return cap;} - public long getCost() {return cost;} - public long getFlow() {return g[to][rev].cap;} } - private static final long INF = Long.MAX_VALUE; + private static final class IntPair { + final int first, second; + IntPair(int first, int second) { this.first = first; this.second = second; } + } - private final int n; - private int m; - private final java.util.ArrayList edges; - private final int[] count; - private final WeightedCapEdge[][] g; - private final long[] potential; + public static final class FlowAndCost { + public final long flow, cost; + FlowAndCost(long flow, long cost) { this.flow = flow; this.cost = cost; } + @Override + public boolean equals(Object o) { + if (o instanceof FlowAndCost) { + FlowAndCost c = (FlowAndCost) o; + return flow == c.flow && cost == c.cost; + } + return false; + } + } + + static final long INF = Long.MAX_VALUE; - private final long[] dist; - private final WeightedCapEdge[] prev; + private final int n; + private final java.util.ArrayList pos; + private final java.util.ArrayList[] g; + @SuppressWarnings("unchecked") public MinCostFlow(int n) { this.n = n; - this.edges = new java.util.ArrayList<>(); - this.count = new int[n]; - this.g = new WeightedCapEdge[n][]; - this.potential = new long[n]; - this.dist = new long[n]; - this.prev = new WeightedCapEdge[n]; + this.pos = new java.util.ArrayList<>(); + this.g = new java.util.ArrayList[n]; + for (int i = 0; i < n; i++) { + this.g[i] = new java.util.ArrayList<>(); + } } public int addEdge(int from, int to, long cap, long cost) { @@ -50,144 +64,130 @@ public int addEdge(int from, int to, long cap, long cost) { rangeCheck(to, 0, n); nonNegativeCheck(cap, "Capacity"); nonNegativeCheck(cost, "Cost"); - WeightedCapEdge e = new WeightedCapEdge(from, to, cap, cost, count[to]); - count[from]++; count[to]++; - edges.add(e); - return m++; + int m = pos.size(); + pos.add(new IntPair(from, g[from].size())); + int fromId = g[from].size(); + int toId = g[to].size(); + if (from == to) toId++; + g[from].add(new InternalWeightedCapEdge(to, toId, cap, cost)); + g[to].add(new InternalWeightedCapEdge(from, fromId, 0L, -cost)); + return m; } - private void buildGraph() { - for (int i = 0; i < n; i++) { - g[i] = new WeightedCapEdge[count[i]]; - } - int[] idx = new int[n]; - for (WeightedCapEdge e : edges) { - g[e.to][idx[e.to]++] = new WeightedCapEdge(e.to, e.from, 0, -e.cost, idx[e.from]); - g[e.from][idx[e.from]++] = e; - } + private InternalWeightedCapEdge getInternalEdge(int i) { + return g[pos.get(i).first].get(pos.get(i).second); } - private long addFlow; - private long addCost; - - public long[] minCostMaxFlow(int s, int t) { - return minCostFlow(s, t, INF); + private InternalWeightedCapEdge getInternalEdgeReversed(InternalWeightedCapEdge e) { + return g[e.to].get(e.rev); + } + + public WeightedCapEdge getEdge(int i) { + int m = pos.size(); + rangeCheck(i, 0, m); + InternalWeightedCapEdge e = getInternalEdge(i); + InternalWeightedCapEdge re = getInternalEdgeReversed(e); + return new WeightedCapEdge(re.to, e.to, e.cap + re.cap, re.cap, e.cost); } - public long[] minCostFlow(int s, int t, long flowLimit) { - rangeCheck(s, 0, n); - rangeCheck(t, 0, n); - if (s == t) { - throw new IllegalArgumentException(String.format("s = t = %d", s)); - } - nonNegativeCheck(flowLimit, "Flow"); - buildGraph(); - long flow = 0; - long cost = 0; - while (true) { - dijkstra(s, t, flowLimit - flow); - if (addFlow == 0) break; - flow += addFlow; - cost += addFlow * addCost; - } - return new long[]{flow, cost}; + public WeightedCapEdge[] getEdges() { + WeightedCapEdge[] res = new WeightedCapEdge[pos.size()]; + java.util.Arrays.setAll(res, this::getEdge); + return res; } - public java.util.ArrayList minCostSlope(int s, int t) { + public FlowAndCost minCostMaxFlow(int s, int t) { + return minCostFlow(s, t, INF); + } + public FlowAndCost minCostFlow(int s, int t, long flowLimit) { + return minCostSlope(s, t, flowLimit).getLast(); + } + java.util.LinkedList minCostSlope(int s, int t) { return minCostSlope(s, t, INF); } - public java.util.ArrayList minCostSlope(int s, int t, long flowLimit) { + public java.util.LinkedList minCostSlope(int s, int t, long flowLimit) { rangeCheck(s, 0, n); rangeCheck(t, 0, n); if (s == t) { - throw new IllegalArgumentException(String.format("s = t = %d", s)); + throw new IllegalArgumentException( + String.format("%d and %d is the same vertex.", s, t) + ); } - nonNegativeCheck(flowLimit, "Flow"); - buildGraph(); - java.util.ArrayList slope = new java.util.ArrayList<>(); - long prevCost = -1; + long[] dual = new long[n]; + long[] dist = new long[n]; + int[] pv = new int[n]; + int[] pe = new int[n]; + boolean[] vis = new boolean[n]; long flow = 0; - long cost = 0; - while (true) { - slope.add(new long[]{flow, cost}); - dijkstra(s, t, flowLimit - flow); - if (addFlow == 0) return slope; - flow += addFlow; - cost += addFlow * addCost; - if (addCost == prevCost) { - slope.remove(slope.size() - 1); + long cost = 0, prev_cost = -1; + java.util.LinkedList result = new java.util.LinkedList<>(); + result.addLast(new FlowAndCost(flow, cost)); + while (flow < flowLimit) { + if (!dualRef(s, t, dual, dist, pv, pe, vis)) break; + long c = flowLimit - flow; + for (int v = t; v != s; v = pv[v]) { + c = Math.min(c, g[pv[v]].get(pe[v]).cap); + } + for (int v = t; v != s; v = pv[v]) { + InternalWeightedCapEdge e = g[pv[v]].get(pe[v]); + e.cap -= c; + g[v].get(e.rev).cap += c; + } + long d = -dual[s]; + flow += c; + cost += c * d; + if (prev_cost == d) { + result.removeLast(); } - prevCost = addCost; + result.addLast(new FlowAndCost(flow, cost)); + prev_cost = cost; } + return result; } - private void dijkstra(int s, int t, long maxFlow) { - final class State implements Comparable { - final int v; - final long d; - State(int v, long d) {this.v = v; this.d = d;} - public int compareTo(State s) {return d == s.d ? v - s.v : d > s.d ? 1 : -1;} - } + private boolean dualRef(int s, int t, long[] dual, long[] dist, int[] pv, int[] pe, boolean[] vis) { java.util.Arrays.fill(dist, INF); - dist[s] = 0; + java.util.Arrays.fill(pv, -1); + java.util.Arrays.fill(pe, -1); + java.util.Arrays.fill(vis, false); + class State implements Comparable { + final long key; + final int to; + State(long key, int to) { this.key = key; this.to = to; } + public int compareTo(State q) { + return key > q.key ? 1 : -1; + } + }; java.util.PriorityQueue pq = new java.util.PriorityQueue<>(); - pq.add(new State(s, 0l)); + dist[s] = 0; + pq.add(new State(0L, s)); while (pq.size() > 0) { - State st = pq.poll(); - int u = st.v; - if (st.d != dist[u]) continue; - for (WeightedCapEdge e : g[u]) { - if (e.cap <= 0) continue; - int v = e.to; - long nextCost = dist[u] + e.cost + potential[u] - potential[v]; - if (nextCost < dist[v]) { - dist[v] = nextCost; - prev[v] = e; - pq.add(new State(v, dist[v])); + int v = pq.poll().to; + if (vis[v]) continue; + vis[v] = true; + if (v == t) break; + for (int i = 0, deg = g[v].size(); i < deg; i++) { + InternalWeightedCapEdge e = g[v].get(i); + if (vis[e.to] || e.cap == 0) continue; + long cost = e.cost - dual[e.to] + dual[v]; + if (dist[e.to] - dist[v] > cost) { + dist[e.to] = dist[v] + cost; + pv[e.to] = v; + pe[e.to] = i; + pq.add(new State(dist[e.to], e.to)); } } } - if (dist[t] == INF) { - addFlow = 0; - addCost = INF; - return; - } - for (int i = 0; i < n; i++) { - potential[i] += dist[i]; - } - addCost = 0; - addFlow = maxFlow; - for (int v = t; v != s;) { - WeightedCapEdge e = prev[v]; - addCost += e.cost; - addFlow = java.lang.Math.min(addFlow, e.cap); - v = e.from; - } - for (int v = t; v != s;) { - WeightedCapEdge e = prev[v]; - e.cap -= addFlow; - g[v][e.rev].cap += addFlow; - v = e.from; + if (!vis[t]) { + return false; } - } - public void clearFlow() { - java.util.Arrays.fill(potential, 0); - for (WeightedCapEdge e : edges) { - long flow = e.getFlow(); - e.cap += flow; - g[e.to][e.rev].cap -= flow; + for (int v = 0; v < n; v++) { + if (!vis[v]) continue; + dual[v] -= dist[t] - dist[v]; } - } - - public WeightedCapEdge getEdge(int i) { - rangeCheck(i, 0, m); - return edges.get(i); - } - - public java.util.ArrayList getEdges() { - return edges; + return true; } private void rangeCheck(int i, int minInlusive, int maxExclusive) { diff --git a/MinCostFlow/MinCostFlowTest.java b/MinCostFlow/MinCostFlowTest.java new file mode 100644 index 0000000..9a773b1 --- /dev/null +++ b/MinCostFlow/MinCostFlowTest.java @@ -0,0 +1,72 @@ +import static org.junit.Assert.assertEquals; + +import java.util.LinkedList; + +import org.junit.Test; + +public class MinCostFlowTest { + @Test + public void simple() { + MinCostFlow g = new MinCostFlow(4); + g.addEdge(0, 1, 1, 1); + g.addEdge(0, 2, 1, 1); + g.addEdge(1, 3, 1, 1); + g.addEdge(2, 3, 1, 1); + g.addEdge(1, 2, 1, 1); + LinkedList expect = new LinkedList<>(); + expect.add(new MinCostFlow.FlowAndCost(0, 0)); + expect.add(new MinCostFlow.FlowAndCost(2, 4)); + assertEquals(expect, g.minCostSlope(0, 3, 10)); + MinCostFlow.WeightedCapEdge e; + + e = new MinCostFlow.WeightedCapEdge(0, 1, 1, 1, 1); + assertEquals(e, g.getEdge(0)); + e = new MinCostFlow.WeightedCapEdge(0, 2, 1, 1, 1); + assertEquals(e, g.getEdge(1)); + e = new MinCostFlow.WeightedCapEdge(1, 3, 1, 1, 1); + assertEquals(e, g.getEdge(2)); + e = new MinCostFlow.WeightedCapEdge(2, 3, 1, 1, 1); + assertEquals(e, g.getEdge(3)); + e = new MinCostFlow.WeightedCapEdge(1, 2, 1, 0, 1); + assertEquals(e, g.getEdge(4)); + } + + @Test + public void usage() { + { + MinCostFlow g = new MinCostFlow(2); + g.addEdge(0, 1, 1, 2); + assertEquals(new MinCostFlow.FlowAndCost(1, 2), g.minCostMaxFlow(0, 1)); + } + { + MinCostFlow g = new MinCostFlow(2); + g.addEdge(0, 1, 1, 2); + LinkedList expect = new LinkedList<>(); + expect.add(new MinCostFlow.FlowAndCost(0, 0)); + expect.add(new MinCostFlow.FlowAndCost(1, 2)); + assertEquals(expect, g.minCostSlope(0, 1)); + } + } + + @Test + public void outOfRange() { + MinCostFlow g = new MinCostFlow(10); + try { + g.minCostSlope(-1, 3); + throw new AssertionError(); + } catch (Exception e) {} + try { + g.minCostSlope(3, 3); + throw new AssertionError(); + } catch (Exception e) {} + } + + @Test + public void selfLoop() { + MinCostFlow g = new MinCostFlow(3); + assertEquals(0, g.addEdge(0, 0, 100, 123)); + + MinCostFlow.WeightedCapEdge e = new MinCostFlow.WeightedCapEdge(0, 0, 100, 0, 123); + assertEquals(e, g.getEdge(0)); + } +} diff --git a/MinCostFlow/Readme.md b/MinCostFlow/Readme.md index 88508ad..4244870 100644 --- a/MinCostFlow/Readme.md +++ b/MinCostFlow/Readme.md @@ -35,12 +35,12 @@ public int addEdge(int from, int to, long cap, long cost) ```java // (1) -public long[] minCostMaxFlow(int s, int t) +public MinCostFlow.FlowAndCost minCostMaxFlow(int s, int t) // (2) -public long[] minCostFlow(int s, int t, long flowLimit) +public MinCostFlow.FlowAndCost minCostFlow(int s, int t, long flowLimit) ``` -`s` から `t` へ流せるだけ流し、その流量とコストを返す。返り値は長さ 2 の配列で表現され、中身は `{flow, cost}` である。 +`s` から `t` へ流せるだけ流し、その流量とコストを返す。返り値は `MinCostFlow.FlowAndCost` であり,メンバに `long flow` と `long cost` を持つ. - (1): `s` から `t` へ流せるだけ流す - (2): `s` から `t` へ流量 `flowLimit` まで流せるだけ流す @@ -57,9 +57,9 @@ public long[] minCostFlow(int s, int t, long flowLimit) ```java // (1) -public java.util.ArrayList minCostSlope(int s, int t) +public java.util.ArrayList minCostSlope(int s, int t) // (2) -public java.util.ArrayList minCostSlope(int s, int t, long flowLimit) +public java.util.ArrayList minCostSlope(int s, int t, long flowLimit) ``` 返り値に流量とコストの関係の折れ線が入る。全ての `x` について、流量 `x` の時の最小コストを `g(x)` とすると、`{x, g(x)}` は返り値を折れ線として見たものに含まれる。返り値のリストは以下のような性質を満たします。 @@ -73,24 +73,12 @@ public java.util.ArrayList minCostSlope(int s, int t, long flowLimit) 制約 - `s != t` -- `minCostSlope` や `minCostFlow` を合わせて複数回呼んだときの挙動は未定義 (複数回フローを流す場合は後述の `clearFlow` メソッドによりフローをリセットしてください) +- `minCostSlope` や `minCostFlow` を合わせて複数回呼んだときの挙動は未定義 計算量 `F` を流量、`m` を追加した辺の本数として $O(F (n + m) \log n)$ -### clearFlow - -```java -public void clearFlow() -``` - -全ての辺に流れているフローを 0 にします。 - -計算量 - -`m` を追加した辺の本数として $O(n + m)$ - ### getEdge / getEdges `getEdge`、`getEdges` の返り値には以下のメソッドを持つ `MinCostFlow.WeightedCapEdge` 型が用いられています。 @@ -98,29 +86,29 @@ public void clearFlow() ```java class WeightedCapEdge { // (1) - public int getFrom() + public final int from; // (2) - public int getTo() + public final int to; // (3) - public long getCap() + public final long cap; // (4) - public long getCost() + public final long flow; // (4) - public long getFlow() + public final long cost; }; ``` -- (1): 始点を返します。計算量: $O(1)$ -- (2): 終点を返します。計算量: $O(1)$ -- (3): 現在の容量を返します。計算量: $O(1)$ -- (4): コストを返します。計算量: $O(1)$ +- (1): 辺の始点を返します。計算量: $O(1)$ +- (2): 辺の終点を返します。計算量: $O(1)$ +- (3): 辺の容量を返します。計算量: $O(1)$ +- (4): 辺のコストを返します。計算量: $O(1)$ - (5): 現在の流量を返しまず。計算量: $O(1)$ ```java // (1) -public CapEdge getEdge(int i) +public WeightedCapEdge getEdge(int i) // (2) -public java.util.ArrayList getEdges() +public WeightedCapEdge[] getEdges() ``` 今の内部の辺の状態を返す。辺の順番は `addEdge` で追加された順番と同一。 @@ -132,8 +120,8 @@ public java.util.ArrayList getEdges() 計算量 - (1): $O(1)$ -- (2): $O(1)$ +- (2): $O(m)$ 使用例 -[AtCoder Library Practice Contest E - MinCostFlow](https://atcoder.jp/contests/practice2/submissions/16654383) +[AtCoder Library Practice Contest E - MinCostFlow](https://atcoder.jp/contests/practice2/submissions/20811599) diff --git a/ModInt/.DS_Store b/ModInt/.DS_Store new file mode 100644 index 0000000..8415c2b Binary files /dev/null and b/ModInt/.DS_Store differ diff --git a/ModInt/ModInt.java b/ModInt/ModInt.java index c5850d8..fcbf5dc 100644 --- a/ModInt/ModInt.java +++ b/ModInt/ModInt.java @@ -1,467 +1,437 @@ -/** - * @verified - *
    - *
  • https://atcoder.jp/contests/arc050/tasks/arc050_c - *
  • https://atcoder.jp/contests/abc129/tasks/abc129_f - *
- */ -class ModIntFactory { - private final ModArithmetic ma; - private final int mod; - - public ModIntFactory(int mod) { - this.ma = ModArithmetic.of(mod); - this.mod = mod; - } - - public ModInt create(long value) { - if ((value %= mod) < 0) value += mod; - if (ma instanceof ModArithmetic.ModArithmeticMontgomery) { - return new ModInt(((ModArithmetic.ModArithmeticMontgomery) ma).generate(value)); - } - return new ModInt((int) value); - } - - class ModInt { - private int value; - private ModInt(int value) { - this.value = value; - } - public int mod() { - return mod; - } - public int value() { - if (ma instanceof ModArithmetic.ModArithmeticMontgomery) { - return ((ModArithmetic.ModArithmeticMontgomery) ma).reduce(value); - } - return value; - } - public ModInt add(ModInt mi) { - return new ModInt(ma.add(value, mi.value)); - } - public ModInt add(ModInt mi1, ModInt mi2) { - return new ModInt(ma.add(value, mi1.value)).addAsg(mi2); - } - public ModInt add(ModInt mi1, ModInt mi2, ModInt mi3) { - return new ModInt(ma.add(value, mi1.value)).addAsg(mi2).addAsg(mi3); - } - public ModInt add(ModInt mi1, ModInt mi2, ModInt mi3, ModInt mi4) { - return new ModInt(ma.add(value, mi1.value)).addAsg(mi2).addAsg(mi3).addAsg(mi4); - } - public ModInt add(ModInt mi1, ModInt... mis) { - ModInt mi = add(mi1); - for (ModInt m : mis) mi.addAsg(m); - return mi; - } - public ModInt add(long mi) { - return new ModInt(ma.add(value, ma.remainder(mi))); - } - public ModInt sub(ModInt mi) { - return new ModInt(ma.sub(value, mi.value)); - } - public ModInt sub(long mi) { - return new ModInt(ma.sub(value, ma.remainder(mi))); - } - public ModInt mul(ModInt mi) { - return new ModInt(ma.mul(value, mi.value)); - } - public ModInt mul(ModInt mi1, ModInt mi2) { - return new ModInt(ma.mul(value, mi1.value)).mulAsg(mi2); - } - public ModInt mul(ModInt mi1, ModInt mi2, ModInt mi3) { - return new ModInt(ma.mul(value, mi1.value)).mulAsg(mi2).mulAsg(mi3); - } - public ModInt mul(ModInt mi1, ModInt mi2, ModInt mi3, ModInt mi4) { - return new ModInt(ma.mul(value, mi1.value)).mulAsg(mi2).mulAsg(mi3).mulAsg(mi4); - } - public ModInt mul(ModInt mi1, ModInt... mis) { - ModInt mi = mul(mi1); - for (ModInt m : mis) mi.mulAsg(m); - return mi; - } - public ModInt mul(long mi) { - return new ModInt(ma.mul(value, ma.remainder(mi))); - } - public ModInt div(ModInt mi) { - return new ModInt(ma.div(value, mi.value)); - } - public ModInt div(long mi) { - return new ModInt(ma.div(value, ma.remainder(mi))); - } - public ModInt inv() { - return new ModInt(ma.inv(value)); - } - public ModInt pow(long b) { - return new ModInt(ma.pow(value, b)); - } - public ModInt addAsg(ModInt mi) { - this.value = ma.add(value, mi.value); - return this; - } - public ModInt addAsg(ModInt mi1, ModInt mi2) { - return addAsg(mi1).addAsg(mi2); - } - public ModInt addAsg(ModInt mi1, ModInt mi2, ModInt mi3) { - return addAsg(mi1).addAsg(mi2).addAsg(mi3); - } - public ModInt addAsg(ModInt mi1, ModInt mi2, ModInt mi3, ModInt mi4) { - return addAsg(mi1).addAsg(mi2).addAsg(mi3).addAsg(mi4); - } - public ModInt addAsg(ModInt... mis) { - for (ModInt m : mis) addAsg(m); - return this; - } - public ModInt addAsg(long mi) { - this.value = ma.add(value, ma.remainder(mi)); - return this; - } - public ModInt subAsg(ModInt mi) { - this.value = ma.sub(value, mi.value); - return this; - } - public ModInt subAsg(long mi) { - this.value = ma.sub(value, ma.remainder(mi)); - return this; - } - public ModInt mulAsg(ModInt mi) { - this.value = ma.mul(value, mi.value); - return this; - } - public ModInt mulAsg(ModInt mi1, ModInt mi2) { - return mulAsg(mi1).mulAsg(mi2); - } - public ModInt mulAsg(ModInt mi1, ModInt mi2, ModInt mi3) { - return mulAsg(mi1).mulAsg(mi2).mulAsg(mi3); - } - public ModInt mulAsg(ModInt mi1, ModInt mi2, ModInt mi3, ModInt mi4) { - return mulAsg(mi1).mulAsg(mi2).mulAsg(mi3).mulAsg(mi4); - } - public ModInt mulAsg(ModInt... mis) { - for (ModInt m : mis) mulAsg(m); - return this; - } - public ModInt mulAsg(long mi) { - this.value = ma.mul(value, ma.remainder(mi)); - return this; - } - public ModInt divAsg(ModInt mi) { - this.value = ma.div(value, mi.value); - return this; - } - public ModInt divAsg(long mi) { - this.value = ma.div(value, ma.remainder(mi)); - return this; - } - @Override - public String toString() { - return String.valueOf(value()); - } - @Override - public boolean equals(Object o) { - if (o instanceof ModInt) { - ModInt mi = (ModInt) o; - return mod() == mi.mod() && value() == mi.value(); - } - return false; - } - @Override - public int hashCode() { - return (1 * 37 + mod()) * 37 + value(); - } - } - - private interface ModArithmetic { - public int mod(); - public int remainder(long value); - public int add(int a, int b); - public int sub(int a, int b); - public int mul(int a, int b); - public default int div(int a, int b) { - return mul(a, inv(b)); - } - public int inv(int a); - public int pow(int a, long b); - - public static ModArithmetic of(int mod) { - if (mod <= 0) { - throw new IllegalArgumentException(); - } else if (mod == 1) { - return new ModArithmetic1(); - } else if (mod == 2) { - return new ModArithmetic2(); - } else if (mod == 998244353) { - return new ModArithmetic998244353(); - } else if (mod == 1000000007) { - return new ModArithmetic1000000007(); - } else if ((mod & 1) == 1) { - return new ModArithmeticMontgomery(mod); - } else { - return new ModArithmeticBarrett(mod); - } - } - - static final class ModArithmetic1 implements ModArithmetic { - public int mod() {return 1;} - public int remainder(long value) {return 0;} - public int add(int a, int b) {return 0;} - public int sub(int a, int b) {return 0;} - public int mul(int a, int b) {return 0;} - public int inv(int a) {throw new ArithmeticException("divide by zero");} - public int pow(int a, long b) {return 0;} - } - static final class ModArithmetic2 implements ModArithmetic { - public int mod() {return 2;} - public int remainder(long value) {return (int) (value & 1);} - public int add(int a, int b) {return a ^ b;} - public int sub(int a, int b) {return a ^ b;} - public int mul(int a, int b) {return a & b;} - public int inv(int a) { - if (a == 0) throw new ArithmeticException("divide by zero"); - return a; - } - public int pow(int a, long b) { - if (b == 0) return 1; - return a; - } - } - static final class ModArithmetic998244353 implements ModArithmetic { - private final int mod = 998244353; - public int mod() { - return mod; - } - public int remainder(long value) { - return (int) ((value %= mod) < 0 ? value + mod : value); - } - public int add(int a, int b) { - int res = a + b; - return res >= mod ? res - mod : res; - } - public int sub(int a, int b) { - int res = a - b; - return res < 0 ? res + mod : res; - } - public int mul(int a, int b) { - return (int) (((long) a * b) % mod); - } - public int inv(int a) { - int b = mod; - long u = 1, v = 0; - while (b >= 1) { - long t = a / b; - a -= t * b; - int tmp1 = a; a = b; b = tmp1; - u -= t * v; - long tmp2 = u; u = v; v = tmp2; - } - u %= mod; - if (a != 1) { - throw new ArithmeticException("divide by zero"); - } - return (int) (u < 0 ? u + mod : u); - } - public int pow(int a, long b) { - if (b < 0) throw new ArithmeticException("negative power"); - long res = 1; - long pow2 = a; - long idx = 1; - while (b > 0) { - long lsb = b & -b; - for (; lsb != idx; idx <<= 1) { - pow2 = (pow2 * pow2) % mod; - } - res = (res * pow2) % mod; - b ^= lsb; - } - return (int) res; - } - } - static final class ModArithmetic1000000007 implements ModArithmetic { - private final int mod = 1000000007; - public int mod() { - return mod; - } - public int remainder(long value) { - return (int) ((value %= mod) < 0 ? value + mod : value); - } - public int add(int a, int b) { - int res = a + b; - return res >= mod ? res - mod : res; - } - public int sub(int a, int b) { - int res = a - b; - return res < 0 ? res + mod : res; - } - public int mul(int a, int b) { - return (int) (((long) a * b) % mod); - } - public int div(int a, int b) { - return mul(a, inv(b)); - } - public int inv(int a) { - int b = mod; - long u = 1, v = 0; - while (b >= 1) { - long t = a / b; - a -= t * b; - int tmp1 = a; a = b; b = tmp1; - u -= t * v; - long tmp2 = u; u = v; v = tmp2; - } - u %= mod; - if (a != 1) { - throw new ArithmeticException("divide by zero"); - } - return (int) (u < 0 ? u + mod : u); - } - public int pow(int a, long b) { - if (b < 0) throw new ArithmeticException("negative power"); - long res = 1; - long pow2 = a; - long idx = 1; - while (b > 0) { - long lsb = b & -b; - for (; lsb != idx; idx <<= 1) { - pow2 = (pow2 * pow2) % mod; - } - res = (res * pow2) % mod; - b ^= lsb; - } - return (int) res; - } - } - static final class ModArithmeticMontgomery extends ModArithmeticDynamic { - private final long negInv; - private final long r2, r3; - - private ModArithmeticMontgomery(int mod) { - super(mod); - long inv = 0; - long s = 1, t = 0; - for (int i = 0; i < 32; i++) { - if ((t & 1) == 0) { - t += mod; - inv += s; - } - t >>= 1; - s <<= 1; - } - long r = (1l << 32) % mod; - this.negInv = inv; - this.r2 = (r * r) % mod; - this.r3 = (r2 * r) % mod; - } - private int generate(long x) { - return reduce(x * r2); - } - private int reduce(long x) { - x = (x + ((x * negInv) & 0xffff_ffffl) * mod) >>> 32; - return (int) (x < mod ? x : x - mod); - } - @Override - public int remainder(long value) { - return generate((value %= mod) < 0 ? value + mod : value); - } - @Override - public int mul(int a, int b) { - return reduce((long) a * b); - } - @Override - public int inv(int a) { - a = super.inv(a); - return reduce(a * r3); - } - @Override - public int pow(int a, long b) { - return generate(super.pow(a, b)); - } - } - static final class ModArithmeticBarrett extends ModArithmeticDynamic { - private static final long mask = 0xffff_ffffl; - private final long mh; - private final long ml; - private ModArithmeticBarrett(int mod) { - super(mod); - /** - * m = floor(2^64/mod) - * 2^64 = p*mod + q, 2^32 = a*mod + b - * => (a*mod + b)^2 = p*mod + q - * => p = mod*a^2 + 2ab + floor(b^2/mod) - */ - long a = (1l << 32) / mod; - long b = (1l << 32) % mod; - long m = a * a * mod + 2 * a * b + (b * b) / mod; - mh = m >>> 32; - ml = m & mask; - } - private int reduce(long x) { - long z = (x & mask) * ml; - z = (x & mask) * mh + (x >>> 32) * ml + (z >>> 32); - z = (x >>> 32) * mh + (z >>> 32); - x -= z * mod; - return (int) (x < mod ? x : x - mod); - } - @Override - public int remainder(long value) { - return (int) ((value %= mod) < 0 ? value + mod : value); - } - @Override - public int mul(int a, int b) { - return reduce((long) a * b); - } - } - static class ModArithmeticDynamic implements ModArithmetic { - final int mod; - public ModArithmeticDynamic(int mod) { - this.mod = mod; - } - public int mod() { - return mod; - } - public int remainder(long value) { - return (int) ((value %= mod) < 0 ? value + mod : value); - } - public int add(int a, int b) { - int sum = a + b; - return sum >= mod ? sum - mod : sum; - } - public int sub(int a, int b) { - int sum = a - b; - return sum < 0 ? sum + mod : sum; - } - public int mul(int a, int b) { - return (int) (((long) a * b) % mod); - } - public int inv(int a) { - int b = mod; - long u = 1, v = 0; - while (b >= 1) { - long t = a / b; - a -= t * b; - int tmp1 = a; a = b; b = tmp1; - u -= t * v; - long tmp2 = u; u = v; v = tmp2; - } - u %= mod; - if (a != 1) { - throw new ArithmeticException("divide by zero"); - } - return (int) (u < 0 ? u + mod : u); - } - public int pow(int a, long b) { - if (b < 0) throw new ArithmeticException("negative power"); - int res = 1; - int pow2 = a; - long idx = 1; - while (b > 0) { - long lsb = b & -b; - for (; lsb != idx; idx <<= 1) { - pow2 = mul(pow2, pow2); - } - res = mul(res, pow2); - b ^= lsb; - } - return res; - } - } - } +import java.util.*; + +import java.util.ArrayList; + +/** + * @verified + *
    + *
  • https://atcoder.jp/contests/m-solutions2019/tasks/m_solutions2019_c : (M = 1000000007) + *
  • https://atcoder.jp/contests/abc172/tasks/abc172_e : (M = 1000000007) + *
  • https://atcoder.jp/contests/abc129/tasks/abc129_f : (2 <= M <= 1000000000) + *
  • https://atcoder.jp/contests/arc050/tasks/arc050_c : (2 <= M <= 1000000000) + *
  • https://atcoder.jp/contests/arc012/tasks/arc012_4 : (1 <= M <= 1000000007) + *
  • https://atcoder.jp/contests/abc042/tasks/arc058_b : (M = 1000000007, combination ver.) + *
+ */ +class ModIntFactory { + private final ModArithmetic ma; + private final int mod; + + private final boolean usesMontgomery; + private final ModArithmetic.ModArithmeticMontgomery maMontgomery; + + private ArrayList factorial; + + public ModIntFactory(int mod) { + this.ma = ModArithmetic.of(mod); + this.usesMontgomery = ma instanceof ModArithmetic.ModArithmeticMontgomery; + this.maMontgomery = usesMontgomery ? (ModArithmetic.ModArithmeticMontgomery) ma : null; + this.mod = mod; + + this.factorial = new ArrayList<>(); + } + + public ModInt create(long value) { + if ((value %= mod) < 0) value += mod; + if (usesMontgomery) { + return new ModInt(maMontgomery.generate(value)); + } else { + return new ModInt((int) value); + } + } + + private void prepareFactorial(int max){ + factorial.ensureCapacity(max+1); + if(factorial.size()==0) factorial.add(1); + if (usesMontgomery) { + for(int i=factorial.size(); i<=max; i++){ + factorial.add(ma.mul(factorial.get(i-1), maMontgomery.generate(i))); + } + } else { + for(int i=factorial.size(); i<=max; i++){ + factorial.add(ma.mul(factorial.get(i-1), i)); + } + } + } + + public ModInt factorial(int i){ + prepareFactorial(i); + return create(factorial.get(i)); + } + + public ModInt permutation(int n, int r){ + if(n < 0 || r < 0 || n < r) return create(0); + prepareFactorial(n); + return create(ma.div(factorial.get(n), factorial.get(r))); + } + public ModInt combination(int n, int r){ + if(n < 0 || r < 0 || n < r) return create(0); + prepareFactorial(n); + return create(ma.div(factorial.get(n), ma.mul(factorial.get(r),factorial.get(n-r)))); + } + + public int getMod() { + return mod; + } + + public class ModInt { + private int value; + private ModInt(int value) { + this.value = value; + } + public int mod() { + return mod; + } + public int value() { + if (usesMontgomery) { + return maMontgomery.reduce(value); + } + return value; + } + public ModInt add(ModInt mi) { + return new ModInt(ma.add(value, mi.value)); + } + public ModInt add(ModInt mi1, ModInt mi2) { + return new ModInt(ma.add(value, mi1.value)).addAsg(mi2); + } + public ModInt add(ModInt mi1, ModInt mi2, ModInt mi3) { + return new ModInt(ma.add(value, mi1.value)).addAsg(mi2).addAsg(mi3); + } + public ModInt add(ModInt mi1, ModInt mi2, ModInt mi3, ModInt mi4) { + return new ModInt(ma.add(value, mi1.value)).addAsg(mi2).addAsg(mi3).addAsg(mi4); + } + public ModInt add(ModInt mi1, ModInt... mis) { + ModInt mi = add(mi1); + for (ModInt m : mis) mi.addAsg(m); + return mi; + } + public ModInt add(long mi) { + return new ModInt(ma.add(value, ma.remainder(mi))); + } + public ModInt sub(ModInt mi) { + return new ModInt(ma.sub(value, mi.value)); + } + public ModInt sub(long mi) { + return new ModInt(ma.sub(value, ma.remainder(mi))); + } + public ModInt mul(ModInt mi) { + return new ModInt(ma.mul(value, mi.value)); + } + public ModInt mul(ModInt mi1, ModInt mi2) { + return new ModInt(ma.mul(value, mi1.value)).mulAsg(mi2); + } + public ModInt mul(ModInt mi1, ModInt mi2, ModInt mi3) { + return new ModInt(ma.mul(value, mi1.value)).mulAsg(mi2).mulAsg(mi3); + } + public ModInt mul(ModInt mi1, ModInt mi2, ModInt mi3, ModInt mi4) { + return new ModInt(ma.mul(value, mi1.value)).mulAsg(mi2).mulAsg(mi3).mulAsg(mi4); + } + public ModInt mul(ModInt mi1, ModInt... mis) { + ModInt mi = mul(mi1); + for (ModInt m : mis) mi.mulAsg(m); + return mi; + } + public ModInt mul(long mi) { + return new ModInt(ma.mul(value, ma.remainder(mi))); + } + public ModInt div(ModInt mi) { + return new ModInt(ma.div(value, mi.value)); + } + public ModInt div(long mi) { + return new ModInt(ma.div(value, ma.remainder(mi))); + } + public ModInt inv() { + return new ModInt(ma.inv(value)); + } + public ModInt pow(long b) { + return new ModInt(ma.pow(value, b)); + } + public ModInt addAsg(ModInt mi) { + this.value = ma.add(value, mi.value); + return this; + } + public ModInt addAsg(ModInt mi1, ModInt mi2) { + return addAsg(mi1).addAsg(mi2); + } + public ModInt addAsg(ModInt mi1, ModInt mi2, ModInt mi3) { + return addAsg(mi1).addAsg(mi2).addAsg(mi3); + } + public ModInt addAsg(ModInt mi1, ModInt mi2, ModInt mi3, ModInt mi4) { + return addAsg(mi1).addAsg(mi2).addAsg(mi3).addAsg(mi4); + } + public ModInt addAsg(ModInt... mis) { + for (ModInt m : mis) addAsg(m); + return this; + } + public ModInt addAsg(long mi) { + this.value = ma.add(value, ma.remainder(mi)); + return this; + } + public ModInt subAsg(ModInt mi) { + this.value = ma.sub(value, mi.value); + return this; + } + public ModInt subAsg(long mi) { + this.value = ma.sub(value, ma.remainder(mi)); + return this; + } + public ModInt mulAsg(ModInt mi) { + this.value = ma.mul(value, mi.value); + return this; + } + public ModInt mulAsg(ModInt mi1, ModInt mi2) { + return mulAsg(mi1).mulAsg(mi2); + } + public ModInt mulAsg(ModInt mi1, ModInt mi2, ModInt mi3) { + return mulAsg(mi1).mulAsg(mi2).mulAsg(mi3); + } + public ModInt mulAsg(ModInt mi1, ModInt mi2, ModInt mi3, ModInt mi4) { + return mulAsg(mi1).mulAsg(mi2).mulAsg(mi3).mulAsg(mi4); + } + public ModInt mulAsg(ModInt... mis) { + for (ModInt m : mis) mulAsg(m); + return this; + } + public ModInt mulAsg(long mi) { + this.value = ma.mul(value, ma.remainder(mi)); + return this; + } + public ModInt divAsg(ModInt mi) { + this.value = ma.div(value, mi.value); + return this; + } + public ModInt divAsg(long mi) { + this.value = ma.div(value, ma.remainder(mi)); + return this; + } + @Override + public String toString() { + return String.valueOf(value()); + } + @Override + public boolean equals(Object o) { + if (o instanceof ModInt) { + ModInt mi = (ModInt) o; + return mod() == mi.mod() && value() == mi.value(); + } + return false; + } + @Override + public int hashCode() { + return (1 * 37 + mod()) * 37 + value(); + } + } + + private static abstract class ModArithmetic { + abstract int mod(); + abstract int remainder(long value); + abstract int add(int a, int b); + abstract int sub(int a, int b); + abstract int mul(int a, int b); + int div(int a, int b) { + return mul(a, inv(b)); + } + int inv(int a) { + int b = mod(); + if (b == 1) return 0; + long u = 1, v = 0; + while (b >= 1) { + int t = a / b; + a -= t * b; + int tmp1 = a; a = b; b = tmp1; + u -= t * v; + long tmp2 = u; u = v; v = tmp2; + } + if (a != 1) { + throw new ArithmeticException("divide by zero"); + } + return remainder(u); + } + int pow(int a, long b) { + if (b < 0) throw new ArithmeticException("negative power"); + int r = 1; + int x = a; + while (b > 0) { + if ((b & 1) == 1) r = mul(r, x); + x = mul(x, x); + b >>= 1; + } + return r; + } + + static ModArithmetic of(int mod) { + if (mod <= 0) { + throw new IllegalArgumentException(); + } else if (mod == 1) { + return new ModArithmetic1(); + } else if (mod == 2) { + return new ModArithmetic2(); + } else if (mod == 998244353) { + return new ModArithmetic998244353(); + } else if (mod == 1000000007) { + return new ModArithmetic1000000007(); + } else if ((mod & 1) == 1) { + return new ModArithmeticMontgomery(mod); + } else { + return new ModArithmeticBarrett(mod); + } + } + + private static final class ModArithmetic1 extends ModArithmetic { + int mod() {return 1;} + int remainder(long value) {return 0;} + int add(int a, int b) {return 0;} + int sub(int a, int b) {return 0;} + int mul(int a, int b) {return 0;} + int pow(int a, long b) {return 0;} + } + private static final class ModArithmetic2 extends ModArithmetic { + int mod() {return 2;} + int remainder(long value) {return (int) (value & 1);} + int add(int a, int b) {return a ^ b;} + int sub(int a, int b) {return a ^ b;} + int mul(int a, int b) {return a & b;} + } + private static final class ModArithmetic998244353 extends ModArithmetic { + private final int mod = 998244353; + int mod() { + return mod; + } + int remainder(long value) { + return (int) ((value %= mod) < 0 ? value + mod : value); + } + int add(int a, int b) { + int res = a + b; + return res >= mod ? res - mod : res; + } + int sub(int a, int b) { + int res = a - b; + return res < 0 ? res + mod : res; + } + int mul(int a, int b) { + return (int) (((long) a * b) % mod); + } + } + private static final class ModArithmetic1000000007 extends ModArithmetic { + private final int mod = 1000000007; + int mod() { + return mod; + } + int remainder(long value) { + return (int) ((value %= mod) < 0 ? value + mod : value); + } + int add(int a, int b) { + int res = a + b; + return res >= mod ? res - mod : res; + } + int sub(int a, int b) { + int res = a - b; + return res < 0 ? res + mod : res; + } + int mul(int a, int b) { + return (int) (((long) a * b) % mod); + } + } + private static final class ModArithmeticMontgomery extends ModArithmeticDynamic { + private final long negInv; + private final long r2; + + private ModArithmeticMontgomery(int mod) { + super(mod); + long inv = 0; + long s = 1, t = 0; + for (int i = 0; i < 32; i++) { + if ((t & 1) == 0) { + t += mod; + inv += s; + } + t >>= 1; + s <<= 1; + } + long r = (1l << 32) % mod; + this.negInv = inv; + this.r2 = (r * r) % mod; + } + private int generate(long x) { + return reduce(x * r2); + } + private int reduce(long x) { + x = (x + ((x * negInv) & 0xffff_ffffl) * mod) >>> 32; + return (int) (x < mod ? x : x - mod); + } + @Override + int remainder(long value) { + return generate((value %= mod) < 0 ? value + mod : value); + } + @Override + int mul(int a, int b) { + return reduce((long) a * b); + } + @Override + int inv(int a) { + return super.inv(reduce(a)); + } + @Override + int pow(int a, long b) { + return generate(super.pow(a, b)); + } + } + private static final class ModArithmeticBarrett extends ModArithmeticDynamic { + private static final long mask = 0xffff_ffffl; + private final long mh; + private final long ml; + private ModArithmeticBarrett(int mod) { + super(mod); + /** + * m = floor(2^64/mod) + * 2^64 = p*mod + q, 2^32 = a*mod + b + * => (a*mod + b)^2 = p*mod + q + * => p = mod*a^2 + 2ab + floor(b^2/mod) + */ + long a = (1l << 32) / mod; + long b = (1l << 32) % mod; + long m = a * a * mod + 2 * a * b + (b * b) / mod; + mh = m >>> 32; + ml = m & mask; + } + private int reduce(long x) { + long z = (x & mask) * ml; + z = (x & mask) * mh + (x >>> 32) * ml + (z >>> 32); + z = (x >>> 32) * mh + (z >>> 32); + x -= z * mod; + return (int) (x < mod ? x : x - mod); + } + @Override + int remainder(long value) { + return (int) ((value %= mod) < 0 ? value + mod : value); + } + @Override + int mul(int a, int b) { + return reduce((long) a * b); + } + } + private static class ModArithmeticDynamic extends ModArithmetic { + final int mod; + ModArithmeticDynamic(int mod) { + this.mod = mod; + } + int mod() { + return mod; + } + int remainder(long value) { + return (int) ((value %= mod) < 0 ? value + mod : value); + } + int add(int a, int b) { + int sum = a + b; + return sum >= mod ? sum - mod : sum; + } + int sub(int a, int b) { + int sum = a - b; + return sum < 0 ? sum + mod : sum; + } + int mul(int a, int b) { + return (int) (((long) a * b) % mod); + } + } + } } \ No newline at end of file diff --git a/ModInt/README.md b/ModInt/README.md index e2a6268..e9a5236 100644 --- a/ModInt/README.md +++ b/ModInt/README.md @@ -61,6 +61,24 @@ public ModInt create(long value) 値 `value % mod` を持つ `ModInt` を生成します. 計算量: $O(1)$ +```java +public ModInt factorial(int i) +``` + +値 `(i!) % mod` を持つ `ModInt` を生成します. + +```java +public ModInt permutation(int n, int r) +``` + +値 `n P r` を持つ `ModInt` を生成します. + +```java +public ModInt combination(int n, int r) +``` + +値 `n C r` を持つ `ModInt` を生成します. + ### ModIntFactory$ModInt #### mod diff --git a/Pair/Pair.java b/Pair/Pair.java new file mode 100644 index 0000000..c7f1581 --- /dev/null +++ b/Pair/Pair.java @@ -0,0 +1,28 @@ +class Pair, T extends Comparable> implements Comparable>{ + S first; + T second; + + public Pair(S s, T t){ + first = s; + second = t; + } + public S getFirst(){return first;} + public T getSecond(){return second;} + public boolean equals(Object another){ + if(this==another) return true; + if(!(another instanceof Pair)) return false; + Pair otherPair = (Pair)another; + return this.first.equals(otherPair.first) && this.second.equals(otherPair.second); + } + public int compareTo(Pair another){ + java.util.Comparator> comp1 = java.util.Comparator.comparing(Pair::getFirst); + java.util.Comparator> comp2 = comp1.thenComparing(Pair::getSecond); + return comp2.compare(this, another); + } + public int hashCode(){ + return first.hashCode() * 10007 + second.hashCode(); + } + public String toString(){ + return String.format("(%s, %s)", first, second); + } +} \ No newline at end of file diff --git a/Pair/README.md b/Pair/README.md new file mode 100644 index 0000000..61781e0 --- /dev/null +++ b/Pair/README.md @@ -0,0 +1,32 @@ +# クラス Permutation + +本機能は AtCoderLibrary ではなく C++標準ライブラリ `std::pair` の移植です. +2要素の組を表現するオブジェクト型です. + +## コンストラクタ + +### Pair, T extends Comparable> + +```java +public Pair(S s, T t) +``` +2要素 $s, t$ の組を作成します. + +## メソッド + +### equals + +```java +public boolean equals(Object another) +``` +この組がオブジェクト `another` と等しい時にtrueを返す関数です. +ここで, Pairどうしが等しいとは, 第一要素どうしが等しくかつ第二要素どうしが等しいことを指します. + + +### compareTo + +```java +public int compareTo(Pair another) +``` +このオブジェクトと指定されたオブジェクトの順序を比較します. +第一要素どうしが等しくない場合はその順序を, 等しい場合は第二要素どうしの順序を返します(いわゆる辞書順). \ No newline at end of file diff --git a/Permutation/Permutation.java b/Permutation/Permutation.java new file mode 100644 index 0000000..8e85624 --- /dev/null +++ b/Permutation/Permutation.java @@ -0,0 +1,59 @@ +/* +* Verified +* https://atcoder.jp/contests/abc054/submissions/16977824 +*/ +class Permutation implements java.util.Iterator, Iterable { + private int[] next; + + public Permutation(int n) { + next = java.util.stream.IntStream.range(0, n).toArray(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public int[] next() { + int[] r = next.clone(); + next = nextPermutation(next); + return r; + } + + @Override + public java.util.Iterator iterator() { + return this; + } + + public static int[] nextPermutation(int[] a) { + if (a == null || a.length < 2) + return null; + int p = 0; + for (int i = a.length - 2; i >= 0; i--) { + if (a[i] >= a[i + 1]) + continue; + p = i; + break; + } + int q = 0; + for (int i = a.length - 1; i > p; i--) { + if (a[i] <= a[p]) + continue; + q = i; + break; + } + if (p == 0 && q == 0) + return null; + int temp = a[p]; + a[p] = a[q]; + a[q] = temp; + int l = p, r = a.length; + while (++l < --r) { + temp = a[l]; + a[l] = a[r]; + a[r] = temp; + } + return a; + } +} diff --git a/Permutation/README.md b/Permutation/README.md new file mode 100644 index 0000000..acfe48c --- /dev/null +++ b/Permutation/README.md @@ -0,0 +1,63 @@ +# クラス Permutation + +本機能は AtCoderLibrary ではなく C++標準ライブラリ `std::next_permutation` の移植です. + +$N$が与えられたとき,長さ$N$の順列を辞書順に列挙することができます. + +また,拡張 for 文によるイテレーションをサポートしています. +以下のイテレーションは,時間計算量 $O(N * N!)$で動作します. + +``` +Permutation perm = new Permutation(n); +for (int[] p : perm) { + // code here +} +``` + +## コンストラクタ + +### Permutation + +```java +public Permutation(int n) +``` + +長さ$N$の順列を列挙するイテレータを作ります. 計算量 $O(N)$ + +## メソッド + +### hasNext + +```java +public boolean hasNext() +``` + +イテレータが後続の要素を保持する場合に真を返します.計算量 $O(1)$ + +### next + +```java +public int[] next() +``` + +イテレータの後続の要素を取得します.計算量 $O(N)$ + +### iterator + +```java +public Iterator iterator() +``` + +順列を列挙するイテレータを取得します. + +### nextPermutation + +```java +public static int[] nextPermutation(int[] a) +``` + +与えられた順列に対して,辞書順でその直後となるような順列を返却します. +但し,入力の配列が辞書順最大の場合は`null`を返却します. +入力配列中の任意の 2 要素が相異なることを仮定しています. +また,入力で与えられた順列を破壊的に変更することに注意してください. +計算量 $O(N)$ diff --git a/README.md b/README.md index 9b73a84..31cba2b 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ また, ACL外のアルゴリズムについても必要に応じて実装を進めています. +* [高速入出力](https://github.com/NASU41/AtCoderLibraryForJava/tree/master/ContestIO) * [Multiset](https://github.com/NASU41/AtCoderLibraryForJava/tree/master/Multiset) ## プログラムを編集したい方へ diff --git a/SegTree/Readme.md b/SegTree/Readme.md index d0609eb..3faa914 100644 --- a/SegTree/Readme.md +++ b/SegTree/Readme.md @@ -68,7 +68,7 @@ public S prod(int l, int r) `op(a[l], ..., a[r - 1])` を、モノイドの性質を満たしていると仮定して計算します。`l = r` のときは単位元 `e` を返します。 -計算量: $O(n)$ +計算量: $O(\log n)$ 制約: `0 <= l <= r <= n` diff --git a/SegTree/SegTree.java b/SegTree/SegTree.java index 7a458e1..519e68c 100644 --- a/SegTree/SegTree.java +++ b/SegTree/SegTree.java @@ -151,17 +151,21 @@ public void setIndent(int newIndent) { @Override public String toString() { - return toString(1, 0); + return toSimpleString(); } - private String toString(int k, int sp) { - if (k >= N) return indent(sp) + Dat[k]; + public String toDetailedString() { + return toDetailedString(1, 0); + } + + private String toDetailedString(int k, int sp) { + if (k >= N) return indent(sp) + data[k]; String s = ""; - s += toString(k << 1 | 1, sp + indent); + s += toDetailedString(k << 1 | 1, sp + indent); s += "\n"; - s += indent(sp) + Dat[k]; + s += indent(sp) + data[k]; s += "\n"; - s += toString(k << 1 | 0, sp + indent); + s += toDetailedString(k << 1 | 0, sp + indent); return s; } @@ -170,4 +174,15 @@ private static String indent(int n) { while (n --> 0) sb.append(' '); return sb.toString(); } + + public String toSimpleString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0; i < N; i++) { + sb.append(data[i + N]); + if (i < N - 1) sb.append(',').append(' '); + } + sb.append(']'); + return sb.toString(); + } } \ No newline at end of file diff --git a/StringAlgorithm/StringAlgorithm.java b/StringAlgorithm/StringAlgorithm.java index 24862d5..3596c77 100644 --- a/StringAlgorithm/StringAlgorithm.java +++ b/StringAlgorithm/StringAlgorithm.java @@ -24,7 +24,7 @@ private static int[] saDoubling(int[] s) { for(int i = 0;i < n;i++){ sa[i] = i; } - int[] rnk = s; + int[] rnk = java.util.Arrays.copyOf(s, n); int[] tmp = new int[n]; for (int k = 1; k < n; k *= 2) {