0% found this document useful (0 votes)
47 views11 pages

Java Competitive Programming Guide

The Java Competitive Programming Handbook serves as a comprehensive reference for competitive programming, providing templates and optimized algorithms for various problems. It covers topics such as fast I/O, number theory, data structures, graph algorithms, dynamic programming, string algorithms, and geometry. The document includes both naive and optimized solutions, along with practical examples and code snippets for implementation.

Uploaded by

chfelan
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
47 views11 pages

Java Competitive Programming Guide

The Java Competitive Programming Handbook serves as a comprehensive reference for competitive programming, providing templates and optimized algorithms for various problems. It covers topics such as fast I/O, number theory, data structures, graph algorithms, dynamic programming, string algorithms, and geometry. The document includes both naive and optimized solutions, along with practical examples and code snippets for implementation.

Uploaded by

chfelan
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Java Competitive Programming Handbook

Comprehensive Illustrated Reference (Naive vs Optimized • Clickable Links • Contest-Ready Templates)

Table of Contents
Placeholder for table of contents 0
Contest Ready Template (Fast I/O + Utilities)
Use this as your starting file in Codeforces/AtCoder.
import [Link].*;
import [Link].*;

public class Main {


static class FastScanner {
BufferedReader br; StringTokenizer st;
FastScanner() { br = new BufferedReader(new InputStreamReader([Link])); }
String next() throws IOException {
while (st == null || ![Link]()) st = new StringTokenizer([Link]());
return [Link]();
}
int nextInt() throws IOException { return [Link](next()); }
long nextLong() throws IOException { return [Link](next()); }
double nextDouble() throws IOException { return [Link](next()); }
String nextLine() throws IOException { return [Link](); }
}
static final long MOD = 1_000_000_007L;

static long modPow(long a, long e, long m) {


long r = 1 % m; a %= m;
while (e > 0) {
if ((e & 1) == 1) r = (r * a) % m;
a = (a * a) % m; e >>= 1;
}
return r;
}
static long gcd(long a, long b) { while (b != 0) { long t = a % b; a = b; b = t; } return a; }

public static void main(String[] args) throws Exception {


FastScanner fs = new FastScanner();
PrintWriter out = new PrintWriter([Link]);
int t = 1; // t = [Link]();
while (t-- > 0) {
// solve here
}
[Link]();
}
}
Math & Number Theory
Fast Exponentiation (pow) – Naive vs Optimized

Naive:
// Naive O(exp) – not feasible for large exponents
public class PowNaive {
static long pow(long a, long e) {
long r = 1;
for (long i = 0; i < e; i++) r *= a;
return r;
}
public static void main(String[] args) { [Link](pow(2, 10)); }
}

Optimized:
// Optimized Binary Exponentiation O(log exp) – contest-ready
public class PowFast {
static long modPow(long a, long e, long m) {
long r = 1 % m; a %= m;
while (e > 0) {
if ((e & 1) == 1) r = (r * a) % m;
a = (a * a) % m; e >>= 1;
}
return r;
}
public static void main(String[] args) { [Link](modPow(2, 50, 1_000_000_007)); }
}

■ Note: Use binary exponentiation for large exponents; complexity improves from O(e) → O(log e).

nCr modulo prime – Naive vs Optimized

Naive:
// Naive nCr using factorial (no mod) – overflow & slow for large n
public class NCRNaive {
static long fact(int n) { long r = 1; for (int i=2;i<=n;i++) r *= i; return r; }
static long ncr(int n, int r) { return fact(n)/(fact(r)*fact(n-r)); }
public static void main(String[] args) { [Link](ncr(10,3)); }
}

Optimized:
// Optimized nCr mod p with precomputed factorials & inverse factorials
public class NCROpt {
static final int MAX = 100000;
static final long MOD = 1_000_000_007L;
static long[] fact = new long[MAX+1], inv = new long[MAX+1];
static long modPow(long a, long e) {
long r=1; a%=MOD;
while (e>0){ if((e&1)==1) r=(r*a)%MOD; a=(a*a)%MOD; e>>=1; }
return r;
}
static void init() {
fact[0]=1;
for (int i=1;i<=MAX;i++) fact[i]=(fact[i-1]*i)%MOD;
inv[MAX]=modPow(fact[MAX], MOD-2);
for(int i=MAX;i>0;i--) inv[i-1]=(inv[i]*i)%MOD;
}
static long nCr(int n, int r) {
if (r<0||r>n) return 0;
return (((fact[n]*inv[r])%MOD)*inv[n-r])%MOD;
}
public static void main(String[] args){ init(); [Link](nCr(100000, 3)); }
}

■ Note: Precompute factorials up to needed N; complexity per query becomes O(1) after O(N) prep.

Primes: Sieve of Eratosthenes + Smallest Prime Factor (SPF)


public class SieveSPF {
static int N = 1000000;
static boolean[] isPrime = new boolean[N+1];
static int[] spf = new int[N+1]; // smallest prime factor
static void sieve() {
[Link](isPrime, true);
isPrime[0]=isPrime[1]=false;
for(int i=2;i<=N;i++) spf[i]=i;
for (int i=2;i*i<=N;i++) if (isPrime[i]) {
for (int j=i*i;j<=N;j+=i) {
isPrime[j]=false;
if (spf[j]==j) spf[j]=i;
}
}
}
static List<Integer> factorize(int x){
List<Integer> f=new ArrayList<>();
while(x>1){ [Link](spf[x]); x/=spf[x]; }
return f;
}
public static void main(String[] args){ sieve(); [Link](isPrime[9973]); }
}

Practice: CSES – Counting Divisors, CSES – Sum of Divisors


Data Structures
Disjoint Set Union (Union-Find) – Naive vs Optimized

Diagram:

Naive:
// Naive DSU without optimizations
class DSUNaive {
int[] parent;
DSUNaive(int n) { parent = new int[n]; for (int i=0;i<n;i++) parent[i]=i; }
int find(int x) { while (x!=parent[x]) x = parent[x]; return x; }
void union(int a,int b){ a=find(a); b=find(b); if(a!=b) parent[b]=a; }
}

Optimized:
// Optimized DSU with path compression + union by size
class DSU {
int[] p, sz;
DSU(int n){ p=new int[n]; sz=new int[n]; for(int i=0;i<n;i++){ p[i]=i; sz[i]=1; } }
int find(int x){ return p[x]==x?x:(p[x]=find(p[x])); }
void union(int a,int b){
a=find(a); b=find(b);
if(a==b) return;
if(sz[a]<sz[b]){ int t=a;a=b;b=t; }
p[b]=a; sz[a]+=sz[b];
}
}

■ Note: Amortized inverse Ackermann (≈ constant). Always enable both optimizations.

Segment Tree (Range Sum) – Build/Query/Update

Diagram:
public class SegTree {
int n; long[] st;
SegTree(int[] a){
n = 1; while(n < [Link]) n <<= 1;
st = new long[2*n];
for (int i=0;i<[Link];i++) st[n+i] = a[i];
for (int i=n-1;i>0;i--) st[i] = st[2*i] + st[2*i+1];
}
long query(int l,int r){ // inclusive l,r
l += n; r += n; long res = 0;
while(l<=r){
if((l&1)==1) res += st[l++];
if((r&1)==0) res += st[r--];
l>>=1; r>>=1;
}
return res;
}
void update(int idx,int val){
int i = idx + n; st[i] = val; i >>= 1;
while(i>0){ st[i] = st[2*i] + st[2*i+1]; i >>= 1; }
}
}

Practice: CSES – Range Sum Queries II, CF 339D – Xenia and Bit Operations

Fenwick Tree (BIT) – Simpler Range Sums


public class Fenwick {
int n; long[] bit;
Fenwick(int n){ this.n=n; bit=new long[n+1]; }
void add(int idx,long delta){
for(int i=idx+1;i<=n;i += i&-i) bit[i]+=delta;
}
long sumPrefix(int idx){
long s=0;
for(int i=idx+1;i>0;i -= i&-i) s+=bit[i];
return s;
}
long rangeSum(int l,int r){ return sumPrefix(r)-sumPrefix(l-1); }
}

■ Note: BIT uses O(n) space, O(log n) updates/queries; easier to code than Segment Tree for sums.
Graph Algorithms
BFS / DFS Templates (Iterative)
import [Link].*;
public class GraphTemplates {
static List<Integer>[] g;
static void bfs(int s){
int n=[Link]; boolean[] vis=new boolean[n];
Queue<Integer> q=new ArrayDeque<>(); [Link](s); vis[s]=true;
while(![Link]()){
int u=[Link]();
for(int v:g[u]) if(!vis[v]){ vis[v]=true; [Link](v); }
}
}
static void dfs(int s){
int n=[Link]; boolean[] vis=new boolean[n];
Deque<Integer> st=new ArrayDeque<>(); [Link](s);
while(![Link]()){
int u=[Link]();
if(vis[u]) continue; vis[u]=true;
for(int v:g[u]) if(!vis[v]) [Link](v);
}
}
}

Dijkstra – Array (O(V^2)) vs PriorityQueue (O((V+E) log V))

Naive:
// Naive O(V^2) – use only for dense graphs or small V
static int[] dijkstraArray(List<int[]>[] g, int s){
int n=[Link]; int INF=1<<30; int[] dist=new int[n]; boolean[] used=new boolean[n];
[Link](dist, INF); dist[s]=0;
for(int it=0; it<n; it++){
int u=-1;
for(int i=0;i<n;i++) if(!used[i] && (u==-1 || dist[i]<dist[u])) u=i;
if (dist[u]==INF) break;
used[u]=true;
for(int[] e:g[u]){
int v=e[0], w=e[1];
if(dist[v] > dist[u]+w) dist[v]=dist[u]+w;
}
}
return dist;
}

Optimized:
// Optimized with PriorityQueue – suitable for large sparse graphs
static int[] dijkstraPQ(List<int[]>[] g, int s){
int n=[Link]; int INF=1<<30; int[] dist=new int[n];
[Link](dist, INF); dist[s]=0;
PriorityQueue<int[]> pq=new PriorityQueue<>([Link](a->a[1]));
[Link](new int[]{s,0});
boolean[] vis=new boolean[n];
while(![Link]()){
int[] cur=[Link](); int u=cur[0]; if(vis[u]) continue; vis[u]=true;
for(int[] e:g[u]){
int v=e[0], w=e[1];
if(dist[v] > dist[u]+w){
dist[v]=dist[u]+w; [Link](new int[]{v, dist[v]});
}
}
}
return dist;
}

Practice: CSES – Shortest Routes I

Minimum Spanning Tree – Kruskal with DSU


static long kruskal(int n, int[][] edges){
[Link](edges, [Link](e->e[2]));
DSU d=new DSU(n); long cost=0; int cnt=0;
for(int[] e:edges){
int u=e[0], v=e[1], w=e[2];
if([Link](u)!=[Link](v)){ [Link](u,v); cost+=w; cnt++; if(cnt==n-1) break; }
}
return cost;
}

Practice: CSES – Road Reparation


Dynamic Programming
0/1 Knapsack – 2D vs 1D Optimization

Naive:
// 2D DP O(nW) with table
static int knapsack2D(int[] wt,int[] val,int W){
int n=[Link]; int[][] dp=new int[n+1][W+1];
for(int i=1;i<=n;i++){
for(int w=0; w<=W; w++){
dp[i][w]=dp[i-1][w];
if(w>=wt[i-1]) dp[i][w]=[Link](dp[i][w], dp[i-1][w-wt[i-1]]+val[i-1]);
}
}
return dp[n][W];
}

Optimized:
// 1D DP O(nW) with reduced memory
static int knapsack1D(int[] wt,int[] val,int W){
int n=[Link]; int[] dp=new int[W+1];
for(int i=0;i<n;i++){
for(int w=W; w>=wt[i]; w--){
dp[w]=[Link](dp[w], dp[w-wt[i]]+val[i]);
}
}
return dp[W];
}

■ Note: Use 1D when only previous row is needed; iterate weights descending.

Longest Increasing Subsequence – O(n^2) vs O(n log n)

Naive:
static int lisN2(int[] a){
int n=[Link]; int[] dp=new int[n]; [Link](dp,1);
int ans=0;
for(int i=0;i<n;i++){
for(int j=0;j<i;j++) if(a[i]>a[j]) dp[i]=[Link](dp[i], dp[j]+1);
ans=[Link](ans, dp[i]);
}
return ans;
}

Optimized:
static int lisNlogN(int[] a){
ArrayList<Integer> d=new ArrayList<>();
for(int x: a){
int i=[Link](d, x);
if(i<0) i = -(i+1);
if(i==[Link]()) [Link](x); else [Link](i, x);
}
return [Link]();
}

Practice: CSES – Increasing Subsequence


String Algorithms
KMP – Prefix Function

Diagram:

public class KMP {


static int[] prefix(String s){
int n=[Link](); int[] pi=new int[n];
for(int i=1;i<n;i++){
int j=pi[i-1];
while(j>0 && [Link](i)!=[Link](j)) j=pi[j-1];
if([Link](i)==[Link](j)) j++;
pi[i]=j;
}
return pi;
}
}

Practice: CSES – String Matching

Rolling Hash (Polynomial Hash)


public class RollingHash {
static final long MOD = 1_000_000_007L;
static final long BASE = 911382323L; // choose random odd < MOD
static long[] p, h;
static void build(String s){
int n=[Link](); p=new long[n+1]; h=new long[n+1];
p[0]=1;
for(int i=0;i<n;i++){
p[i+1]=(p[i]*BASE)%MOD;
h[i+1]=(h[i]*BASE + [Link](i))%MOD;
}
}
static long get(int l,int r){ // [l,r)
return (h[r] - (h[l]*p[r-l])%MOD + MOD)%MOD;
}
}

■ Note: For safety, consider double hashing with two moduli to reduce collisions.
Geometry
Orientation Test (CCW) + Line Intersection
public class GeometryBasics {
static long cross(long ax,long ay,long bx,long by){ return ax*by - ay*bx; }
static int orient(long ax,long ay,long bx,long by,long cx,long cy){
long v = cross(bx-ax, by-ay, cx-ax, cy-ay);
return [Link](v, 0);
}
static boolean onSeg(long ax,long ay,long bx,long by,long px,long py){
return [Link](ax,bx)<=px && px<=[Link](ax,bx) &&
[Link](ay,by)<=py && py<=[Link](ay,by);
}
static boolean inter(long ax,long ay,long bx,long by,long cx,long cy,long dx,long dy){
int o1=orient(ax,ay,bx,by,cx,cy);
int o2=orient(ax,ay,bx,by,dx,dy);
int o3=orient(cx,cy,dx,dy,ax,ay);
int o4=orient(cx,cy,dx,dy,bx,by);
if(o1*o2<0 && o3*o4<0) return true;
if(o1==0 && onSeg(ax,ay,bx,by,cx,cy)) return true;
if(o2==0 && onSeg(ax,ay,bx,by,dx,dy)) return true;
if(o3==0 && onSeg(cx,cy,dx,dy,ax,ay)) return true;
if(o4==0 && onSeg(cx,cy,dx,dy,bx,by)) return true;
return false;
}
}

Practice: CSES – Point Location Test, CSES – Polygon Area

Common Java CP Mistakes (and Fixes)


Pitfalls & Fixes:
• Use long for multiplications; cast before multiply (e.g., (long)a*b).
• Prefer FastScanner + PrintWriter; avoid Scanner for large inputs.
• String concatenation in loops → use StringBuilder.
• Beware of recursion depth; prefer iterative DFS/DP or increase stack if allowed.
• Avoid heavy object allocations in hot loops; use arrays where possible.

Contest Strategy & Checklist


• Read all problems quickly; solve A/B first for momentum.
• If stuck > 15–20 min, switch problems; upsolve later.
• Write from template; avoid re-typing I/O and helpers.
• Test on small custom cases; assert invariants during debug.
• After contest: upsolve 1–2 problems you couldn’t solve.

Quick Reference Sheet


Fast I/O FastScanner + PrintWriter

Math gcd, modPow, nCr mod p (precompute fact & inv)

DSU Path compression + union by size

BIT / SegTree O(log n) updates/queries

Graphs Iterative BFS/DFS; Dijkstra with PQ

DP Knapsack 1D; LIS O(n log n)

Strings KMP prefix; Rolling Hash

Common questions

Powered by AI

Segment trees offer flexible handling of various types of range queries and updates with O(log n) complexity, allowing more general operations than a Fenwick Tree, which is mainly suited for simpler range sum queries. While Fenwick Trees are generally easier to implement due to their simplicity and use of O(n) space, segment trees can handle a broader class of problems albeit requiring more complex implementations and more memory (O(2n) for balanced binary trees).

Precomputing factorials and inverse factorials allows for O(1) complexity per query for combinations (nCr) modulo a prime after an initial O(N) preprocessing step. This enhances efficiency significantly, particularly in applications involving numerous binomial coefficient calculations, by avoiding repeated expensive computations and reducing the possibility of overflow .

The 2D dynamic programming approach for the 0/1 Knapsack problem uses a table of size O(nW), where n is the number of items and W is the maximum weight capacity, leading to higher space usage. In contrast, the 1D approach reduces space complexity to O(W) by utilizing a single array, leveraging the fact that only the previous state is necessary for computation. The 1D approach is often preferred for space efficiency, particularly when only one dimension of state is updated at each step .

The Sieve of Eratosthenes efficiently marks non-prime numbers by iteratively marking multiples of each prime starting from 2. During this process, it assigns the smallest prime factor (SPF) for each composite number the first time it is marked by a prime. This dual functionality is achieved in O(n log log n) time complexity, making it a powerful algorithm for generating prime information up to a specified limit .

Utilizing FastScanner and PrintWriter enhances performance in competitive programming by efficiently managing large input and output. FastScanner reads inputs more rapidly than Scanner due to lower overhead, while PrintWriter provides faster output handling than System.out.println due to buffered output, minimizing the time spent on I/O operations .

Dijkstra's algorithm using a priority queue efficiently handles sparse graphs by managing the priority of next nodes to explore in logarithmic time O((V+E) log V), preventing exhaustive updates of all vertices in each iteration, unlike the array-based method which involves O(V^2) complexity. This allows it to prioritize nodes closer to the source and reduce unnecessary calculations inherent in denser methods, achieving faster convergence .

The rolling hash technique uses polynomial hashing to represent strings as numerical values, making use of properties like modular arithmetic for efficient substring comparisons. This method strategically reduces collisions by distributing hash values evenly using a carefully chosen base and modulus, often incorporating double hashing for further collision reduction. Such methods are critical in string algorithms like pattern matching where large datasets demand high collision resistance .

The KMP algorithm improves pattern matching from a naive O(mn) to O(m+n) by utilizing a prefix table to avoid re-evaluating previously matched portions of the pattern, enhancing efficiency significantly in large text or repetitive pattern scenarios. However, the disadvantage lies in the complexity of its preprocessing step and additional memory overhead required for the prefix table, potentially complicating its implementation compared to a straightforward naive approach .

Modular exponentiation using binary exponentiation reduces time complexity from O(exp) to O(log exp), making it substantially faster for large exponents. This efficiency is accomplished through recursive squaring which cuts down the number of multiplications needed. It is preferred in contests because it allows for computations involving large numbers under mod constraints, which is crucial for exceeding the limits of naive calculations .

The optimized DSU with path compression and union by size improves upon the naive version by reducing the time complexity nearly to constant time for each union or find operation. Path compression reduces the height of trees created by the union operations, and union by size attaches smaller trees under larger ones, both working together to minimize path lengths during find operations, leading to more efficient queries .

You might also like