結果

問題 No.200 カードファイト!
コンテスト
ユーザー 37zigen
提出日時 2026-08-12 21:57:30
言語 Java
(openjdk 25.0.2)
コンパイル:
javac -encoding UTF8 _filename_
実行:
java -ea -Xmx700m -Xss256M -DONLINE_JUDGE=true _class_
結果
AC  
実行時間 495 ms / 2,000 ms
+ 20µs
コード長 16,029 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 2,029 ms
コンパイル使用メモリ 108,988 KB
実行使用メモリ 52,652 KB
最終ジャッジ日時 2026-08-12 21:57:37
合計ジャッジ時間 6,446 ms
ジャッジサーバーID
(参考情報)
judge3_0 / judge2_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 26
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.lang.reflect.Array;
import java.util.AbstractCollection;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
import java.util.function.BiFunction;
import java.util.function.IntBinaryOperator;
import java.util.function.IntFunction;
import java.util.function.IntToDoubleFunction;
import java.util.function.IntToLongFunction;
import java.util.function.IntUnaryOperator;
import java.util.function.LongBinaryOperator;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.random.RandomGenerator;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class Main {
    static MyPrintWriter pw = MyPrintWriter.getInstance();

    static FastScanner sc = FastScanner.getInstance();

    public static void main(String[] args) throws IOException {
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> System.exit(1));
        new Main().run();
        pw.flush();
    }

    void run() {
        int N = sc.nextInt();
        int A = sc.nextInt();
        int[] B = sc.nextInts(A);
        int C = sc.nextInt();
        int[] D = sc.nextInts(C);
        int qA = 1 + (N / A);
        int qC = 1 + (N / C);
        int leftSize = qA * A;
        MinimumCostFlow mf = new MinimumCostFlow(((qA * A) + (qC * C)) + 4);
        for (int i = 0; i < (qA * A); i++) {
            for (int j = 0; j < (qC * C); j++) {
                if (Intervals.hasOverlap((i / A) * A, ((i / A) + 1) * A, (j / C) * C, ((j / C) + 1) * C)) {
                    if (B[i % A] > D[j % C]) {
                        mf.addEdge(i, leftSize + j, -1, 1);
                    } else {
                        mf.addEdge(i, leftSize + j, 0, 1);
                    }
                }
            }
        }
        int s = (qA * A) + (qC * C);
        int t = ((qA * A) + (qC * C)) + 1;
        int s2 = t + 1;
        int t2 = t + 2;
        mf.addEdge(s, s2, 0, N % A);
        mf.addEdge(t2, t, 0, N % C);
        for (int i = 0; i < (qA * A); i++) {
            if ((i / A) == (qA - 1)) {
                mf.addEdge(s2, i, 0, 1);
            } else {
                mf.addEdge(s, i, 0, 1);
            }
        }
        for (int i = 0; i < (qC * C); i++) {
            if ((i / C) == (qC - 1)) {
                mf.addEdge(i + leftSize, t2, 0, 1);
            } else {
                mf.addEdge(i + leftSize, t, 0, 1);
            }
        }
        var ans = mf.minCostFlowSlope(s, t);
        // mf.draw();
        pw.println(-ans.getLast()[1]);
    }
}

class ArrayUtils {
    /**
     * *
     * a[i] = max a となる最小のiを返す。
     *
     * @param a
     * @return  */
    public static int argMax(long[] a) {
        int ret = 0;
        for (int i = 0; i < a.length; ++i) {
            if (a[i] > a[ret]) {
                ret = i;
            }
        }
        return ret;
    }

    public static long max(long[] a) {
        long ret = Long.MIN_VALUE;
        for (int i = 0; i < a.length; ++i) {
            ret = Math.max(ret, a[i]);
        }
        return ret;
    }
}

class FastScanner {
    private static FastScanner instance = null;

    private final InputStream in = System.in;

    private final byte[] buffer = new byte[1 << 16];

    private int ptr = 0;

    private int buflen = 0;

    private FastScanner() {
    }

    public static FastScanner getInstance() {
        if (instance == null) {
            instance = new FastScanner();
        }
        return instance;
    }

    private boolean hasNextByte() {
        if (ptr < buflen) {
            return true;
        }
        ptr = 0;
        try {
            buflen = in.read(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buflen > 0;
    }

    private int readByte() {
        if (hasNextByte()) {
            return buffer[ptr++];
        } else {
            return -1;
        }
    }

    private boolean isPrintableChar(int c) {
        return (33 <= c) && (c <= 126);
    }

    public boolean hasNext() {
        while (hasNextByte() && (!isPrintableChar(buffer[ptr]))) {
            ptr++;
        } 
        return hasNextByte();
    }

    public long nextLong() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        long n = 0;
        boolean minus = false;
        int b = readByte();
        if (b == '-') {
            minus = true;
            b = readByte();
        }
        while ((b >= '0') && (b <= '9')) {
            // n = n * 10 + (b - '0');
            n = ((n << 1) + (n << 3)) + (b - '0');
            b = readByte();
        } 
        return minus ? -n : n;
    }

    public int nextInt() {
        return ((int) (nextLong()));
    }

    public int[] nextInts(int n) {
        int[] a = new int[n];
        for (int i = 0; i < n; ++i) {
            a[i] = nextInt();
        }
        return a;
    }
}

class Intervals {
    /**
     * [l0, r0), [l1, r1) が共通部分を持つか
     *
     * @param l0
     * @param r0
     * @param l1
     * @param r1
     * @return  */
    public static boolean hasOverlap(long l0, long r0, long l1, long r1) {
        return !((r0 <= l1) || (r1 <= l0));
    }
}

/**
 * 流量をFとしてO(F(N+M)log(N+M))。負辺は最初に全て流しきるため、負辺の流量F'が、答えに必要なくてもFに加算される。F'を計算量から消したい場合は、最初から簡約費用にする。
 */
class MinimumCostFlow {
    int N;

    public ArrayList<Edge>[] g;

    public ArrayList<Edge>[] ig;

    public long[] excess;

    public long[] potential;

    long INF = Long.MAX_VALUE / 3;

    Edge[] incommingShortestPathEdge;

    long[] d;

    long base = 0;

    @SuppressWarnings("unchecked")
    public MinimumCostFlow(int N) {
        this.N = N;
        g = new ArrayList[this.N];
        ig = new ArrayList[this.N];
        excess = new long[this.N];
        potential = new long[this.N];
        d = new long[this.N];
        incommingShortestPathEdge = new Edge[this.N];
        for (int i = 0; i < this.N; ++i) {
            g[i] = new ArrayList<>();
            ig[i] = new ArrayList<>();
        }
    }

    public class Edge {
        public int from;

        public int to;

        public long cost;

        public long cap;

        public long flow = 0;

        Edge reverseEdge;

        public Edge(int from, int to, long cost, long cap) {
            this.from = from;
            this.to = to;
            this.cost = cost;
            this.cap = cap;
        }

        public long res() {
            return cap - flow;
        }

        long reducedCost() {
            // 残余ネットワークで、常に簡約費用非負を保つ。
            // 初期状態ではポテンシャル0だが、負辺を流しきっているのでok。
            // cost - potential[to] + potential[from] >= 0
            // potential[to] + cost >= potential[to]
            // なのである固定した頂点からの距離をpotentialとすると非負になる。
            return cost - (potential[to] - potential[from]);
        }

        void addFlow(long add) {
            flow += add;
            reverseEdge.flow -= add;
            if (flow > cap) {
                throw new AssertionError();
            }
            if (reverseEdge.flow > reverseEdge.cap) {
                throw new AssertionError();
            }
            excess[from] -= add;
            excess[to] += add;
        }

        @Override
        public String toString() {
            return (((((((("from " + from) + " to ") + to) + " : flow=") + flow) + ", cap=") + cap) + ", cost=") + cost;
        }
    }

    public void addEdge(int from, int to, long cost, long cap) {
        Edge e = new Edge(from, to, cost, cap);
        Edge ie = new Edge(to, from, -cost, 0);
        e.reverseEdge = ie;
        ie.reverseEdge = e;
        g[from].add(e);
        ig[to].add(ie);
    }

    /**
     * sからtにフローを流したとき、コストは流量の関数として区分線形になる。
     * そこで、その線分の端点を {@code [flow, cost]} として並べたarraylistを返す。
     * 未テスト
     *
     * @param s
     * @param t
     * @return  */
    public ArrayList<long[]> minCostFlowSlope(int s, int t) {
        return minCostFlowSlopeInitialized(s, t, minCostCirculation());
    }

    private ArrayList<long[]> minCostFlowSlopeInitialized(int s, int t, long cost) {
        ArrayList<long[]> ret = new ArrayList<>();
        long flow = 0;
        ret.add(new long[]{ flow, cost });
        if (s == t) {
            return ret;
        }
        while (true) {
            updateDistance(s);
            if (d[t] == INF) {
                break;
            }
            long addFlow = Long.MAX_VALUE;
            {
                int cur = t;
                while (cur != s) {
                    addFlow = Math.min(addFlow, incommingShortestPathEdge[cur].res());
                    cur = incommingShortestPathEdge[cur].from;
                } 
            }
            if (addFlow == 0) {
                break;
            }
            flow += addFlow;
            {
                int cur = t;
                while (cur != s) {
                    incommingShortestPathEdge[cur].addFlow(addFlow);
                    cost += incommingShortestPathEdge[cur].cost * addFlow;
                    cur = incommingShortestPathEdge[cur].from;
                } 
            }
            ret.add(new long[]{ flow, cost });
        } 
        return ret;
    }

    /**
     * 最小費用循環流を求める。
     * 存在しなければnullを返す。
     * 流量をFとしてO(F(N+M)log(N+M))
     *
     * @param flow
     */
    public Long minCostCirculation() {
        // https://atcoder.jp/contests/abc407/submissions/71000667
        // https://atcoder.jp/contests/practice2/submissions/71006911
        for (var edges : g) {
            for (var edge : edges) {
                if (edge.reducedCost() < 0) {
                    edge.addFlow(edge.cap);
                }
            }
        }
        while (ArrayUtils.max(excess) > 0) {
            int src = ArrayUtils.argMax(excess);
            updateDistance(src);
            int dst = 0;
            while ((dst < N) && ((d[dst] == INF) || (excess[dst] >= 0))) {
                ++dst;
            } 
            if (dst == N) {
                return null;
            }
            long addFlow = Math.min(excess[src], -excess[dst]);
            {
                int cur = dst;
                while (cur != src) {
                    addFlow = Math.min(addFlow, incommingShortestPathEdge[cur].res());
                    cur = incommingShortestPathEdge[cur].from;
                } 
            }
            {
                int cur = dst;
                while (cur != src) {
                    incommingShortestPathEdge[cur].addFlow(addFlow);
                    cur = incommingShortestPathEdge[cur].from;
                } 
            }
        } 
        long ret = 0;
        for (var edges : g) {
            for (var edge : edges) {
                ret += edge.flow * edge.cost;
            }
        }
        return ret + base;
    }

    void updateDistance(int src) {
        Arrays.fill(d, INF);
        Arrays.fill(incommingShortestPathEdge, null);
        d[src] = 0;
        record State(int v, long d) implements Comparable<State> {
            @Override
            public int compareTo(State o) {
                return Long.compare(this.d, o.d);
            }
        }
        PriorityQueue<State> pq = new PriorityQueue<>();
        pq.add(new State(src, 0));
        while (!pq.isEmpty()) {
            State state = pq.poll();
            if (state.d > d[state.v]) {
                continue;
            }
            for (Edge e : g[state.v]) {
                if (e.res() == 0) {
                    continue;
                }
                if (d[e.to] > (state.d + e.reducedCost())) {
                    d[e.to] = state.d + e.reducedCost();
                    pq.add(new State(e.to, d[e.to]));
                    incommingShortestPathEdge[e.to] = e;
                }
            }
            for (Edge e : ig[state.v]) {
                if (e.res() == 0) {
                    continue;
                }
                if (d[e.to] > (state.d + e.reducedCost())) {
                    d[e.to] = state.d + e.reducedCost();
                    pq.add(new State(e.to, d[e.to]));
                    incommingShortestPathEdge[e.to] = e;
                }
            }
        } 
        for (int i = 0; i < N; ++i) {
            if (d[i] != INF) {
                potential[i] += d[i];
            }
        }
    }
}

class MyPrintWriter extends PrintWriter {
    private static MyPrintWriter instance = null;

    private MyPrintWriter() {
        super(System.out);
    }

    public static MyPrintWriter getInstance() {
        if (instance == null) {
            instance = new MyPrintWriter();
        }
        return instance;
    }
}


// --- Original Code ---
// 
// 
// import java.io.IOException;
// import java.util.Arrays;
// 
// import library.tools.FastScanner;
// import library.tools.MergeFiles;
// import library.tools.MyPrintWriter;
// import library.util.ArrayUtils;
// import library.util.Intervals;
// import library.util.collections.LongArrayList;
// import library.util.graph.BipartiteMatching;
// import library.util.graph.MaxFlow;
// import library.util.graph.MaxFlowWithLowerBound;
// import library.util.graph.MinimumCostFlow;
// import library.util.seq.SortedArrays;
// 
// public class Main {
// 	static MyPrintWriter pw = MyPrintWriter.getInstance();
// 	static FastScanner sc = FastScanner.getInstance();
// 
// 	public static void main(String[] args) throws IOException {
// 		new Main().run();
// 		pw.flush();
// 		MergeFiles.export();
// 	}
// 	
// 
// 	void run() {
//         int N = sc.nextInt();
//         int A = sc.nextInt();
//         int[] B = sc.nextInts(A);
//         int C = sc.nextInt();
//         int[] D = sc.nextInts(C);
//         int qA = 1 + N / A;
//         int qC = 1 + N / C;
//         int leftSize = qA * A;
//         MinimumCostFlow mf=new MinimumCostFlow(qA * A + qC * C + 4);
//         for (int i = 0; i < qA * A; i++) {
//         	for (int j = 0; j < qC * C; j++) {
// 				if (Intervals.hasOverlap(i / A * A, (i / A + 1) * A, j / C * C, (j / C + 1) * C)) {
// 					if (B[i % A] > D[j % C]) {
// 						mf.addEdge(i, leftSize + j, -1, 1);
// 					} else {
// 						mf.addEdge(i, leftSize + j, 0, 1);
// 					}
// 				}
// 			}
//         }
//         int s = qA * A + qC * C;
//         int t = qA * A + qC * C + 1;
//         int s2 = t + 1;
//         int t2 = t + 2;
//         mf.addEdge(s, s2, 0, N % A);
//         mf.addEdge(t2, t, 0, N % C);
//         for (int i = 0; i < qA * A; i++) {
//         	if (i / A == qA - 1) {
//         		mf.addEdge(s2, i, 0, 1);
//         	} else {
//         		mf.addEdge(s, i, 0, 1);
//         	}
// 		}
//         for (int i = 0; i < qC * C; i++) {
//         	if (i / C == qC - 1) {
//         		mf.addEdge(i + leftSize, t2, 0, 1);
//         	} else {
//         		mf.addEdge(i + leftSize, t, 0, 1);
//         	}
// 		}
// 
//         var ans = mf.minCostFlowSlope(s, t);
// //        mf.draw();
//         pw.println(-ans.getLast()[1]);
// 	}
// 	
// 	void tr(Object... objects) {
// 		System.out.println(Arrays.deepToString(objects));
// 	}
// }
// 
0