結果

問題 No.3622 Perfect Matching of Crab
コンテスト
ユーザー 37zigen
提出日時 2026-08-15 00:38:22
言語 Java
(openjdk 25.0.2)
コンパイル:
javac -encoding UTF8 _filename_
実行:
java -ea -Xmx700m -Xss256M -DONLINE_JUDGE=true _class_
結果
AC  
実行時間 634 ms / 2,000 ms
+ 381µs
コード長 22,885 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 2,177 ms
コンパイル使用メモリ 110,152 KB
実行使用メモリ 103,876 KB
最終ジャッジ日時 2026-08-15 00:38:33
合計ジャッジ時間 10,486 ms
ジャッジサーバーID
(参考情報)
judge1_0 / judge2_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 2
other AC * 16
権限があれば一括ダウンロードができます

ソースコード

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.lang.reflect.Array;
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.PrimitiveIterator.OfInt;
import java.util.PrimitiveIterator;
import java.util.Queue;
import java.util.Random;
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 T = sc.nextInt();
        for (int TEST = 0; TEST < T; TEST++) {
            int N = sc.nextInt();
            int[] X = new int[2 * N];
            int[] Y = new int[2 * N];
            boolean[] F = new boolean[2 * N];
            for (int i = 0; i < (2 * N); i++) {
                X[i] = sc.nextInt();
                Y[i] = sc.nextInt();
                F[i] = sc.next().charAt(0) == 'x';
            }
            IntTreapMultiSet set0 = new IntTreapMultiSet();
            IntTreapMultiSet set1 = new IntTreapMultiSet();
            for (int i = 0; i < (2 * N); i++) {
                if (F[i]) {
                    set0.add(Y[i]);
                } else {
                    set1.add(X[i]);
                }
            }
            int c0 = 0;
            int c1 = 0;
            IntArrayList list0 = new IntArrayList();
            IntArrayList list1 = new IntArrayList();
            for (var es : set0.entryList()) {
                list0.add(((int) (es.value)));
            }
            for (var es : set1.entryList()) {
                list1.add(((int) (es.value)));
            }
            for (int i = 0; i < list0.size(); i++) {
                if ((list0.get(i) % 2) == 1) {
                    list0.set(i, list0.get(i) - 1);
                    c0++;
                }
            }
            for (int i = 0; i < list1.size(); i++) {
                if ((list1.get(i) % 2) == 1) {
                    list1.set(i, list1.get(i) - 1);
                    c1++;
                }
            }
            int m = Math.min(c0, c1);
            c0 -= m;
            c1 -= m;
            if (c0 > 0) {
                pw.printlnYesNo(ArrayUtils.sum(list1.toArray()) >= c0);
            } else if (c1 > 0) {
                pw.printlnYesNo(ArrayUtils.sum(list0.toArray()) >= c1);
            } else {
                pw.println("Yes");
            }
        }
    }
}

class ArrayUtils {
    public static long sum(int[] a) {
        long ret = 0;
        for (int val : a) {
            ret += val;
        }
        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 String next() {
        if (!hasNext()) {
            throw new 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 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()));
    }
}

/**
 * *
 * tailがa.lengthに比べて小さくなっても配列を取り直さない。
 *
 * @param <T>
 */
class IntArrayList implements Iterable<Integer> {
    @SuppressWarnings("unchecked")
    int defaultCapacity = 16;

    int[] a;

    public int tail = 0;

    public IntArrayList() {
        a = new int[defaultCapacity];
    }

    public void add(int v) {
        if (tail == a.length) {
            resize(2 * a.length);
        }
        a[tail] = v;
        tail++;
    }

    public int get(int id) {
        if ((id < 0) || (id >= tail)) {
            throw new IndexOutOfBoundsException(((((("get(" + id) + ")は添え字") + 0) + "以上") + (tail - 1)) + "以下に違反");
        }
        return a[id];
    }

    public void set(int id, int value) {
        if ((id < 0) || (id >= tail)) {
            throw new IndexOutOfBoundsException();
        }
        a[id] = value;
    }

    void resize(int size) {
        a = Arrays.copyOf(a, size);
    }

    public int size() {
        return tail;
    }

    public int[] toArray() {
        return Arrays.copyOf(a, tail);
    }

    @Override
    public PrimitiveIterator.OfInt iterator() {
        return new PrimitiveIterator.OfInt() {
            int idx = 0;

            @Override
            public boolean hasNext() {
                return idx < tail;
            }

            @Override
            public int nextInt() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                return get(idx++);
            }
        };
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof IntArrayList that)) {
            return false;
        }
        if (tail != that.tail) {
            return false;
        }
        for (int i = 0; i < tail; i++) {
            if (a[i] != that.a[i]) {
                return false;
            }
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = 1;
        for (int i = 0; i < tail; i++) {
            result = (31 * result) + a[i];
        }
        return result;
    }

    @Override
    public String toString() {
        return Arrays.toString(toArray());
    }
}

/**
 * {@code int} に特殊化した Treap ベースのマルチセット。
 * 内部で {@link TreapMap} を使用しています。
 */
class IntTreapMultiSet {
    private final TreapMap map;

    /**
     * 重複を含めた総要素数
     */
    private long size = 0;

    public IntTreapMultiSet() {
        map = new TreapMap();
    }

    /**
     * 要素を {@code repeat} 回追加します。
     * {@code repeat} が負の場合は削除を意味しますが、操作後の個数が負になる場合は {@link AssertionError} を投げます。
     *
     * @param element
     * 		追加する要素
     * @param repeat
     * 		追加する回数
     * @complexity O(log N) (N は異なる要素の数)
     */
    public void add(int element, long repeat) {
        if (repeat == 0) {
            return;
        }
        size += repeat;
        long num = map.getOrDefault(element, 0L) + repeat;
        if (num < 0) {
            throw new AssertionError();
        }
        if (num == 0) {
            map.remove(element);
        } else {
            map.put(element, num);
        }
    }

    /**
     * 要素を1つ追加します。
     *
     * @param element
     * 		追加する要素
     * @complexity O(log N)
     */
    public void add(int element) {
        add(element, 1);
    }

    public static class Entry {
        public final int key;

        public final long value;

        public Entry(int key, long value) {
            this.key = key;
            this.value = value;
        }
    }

    /**
     * キーが要素、値が要素の個数であるエントリーのリストを返します。
     *
     * @return エントリーのリスト
     * @complexity O(N)
     */
    public List<Entry> entryList() {
        List<Entry> res = new ArrayList<>();
        for (var e : map.entryList()) {
            res.add(new Entry(((int) (e.key)), e.value));
        }
        return res;
    }

    /**
     * 異なる要素の数を返します。
     *
     * @return 異なる要素の数
     * @complexity O(1)
     */
    public int numberDistinctElements() {
        return map.size();
    }

    /**
     * 集合が空であるかどうかを返します。
     *
     * @return 空であれば {@code true}
     * @complexity O(1)
     */
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * 集合の状態を文字列として表します。
     * <ul>
     * <li>事前条件: 特になし。</li>
     * <li>事後条件: 特になし。</li>
     * <li>計算量: $O(N)$</li>
     * <li>破壊的変更: なし。</li>
     * </ul>
     *
     * @return 集合の状態を表す文字列
     */
    // 未テスト
    @Override
    public String toString() {
        if (isEmpty()) {
            return "空集合";
        } else {
            StringBuilder sb = new StringBuilder();
            boolean first = true;
            for (var entry : entryList()) {
                if (!first) {
                    sb.append("\n");
                }
                sb.append(entry.key).append(" が ").append(entry.value).append("個");
                first = false;
            }
            return sb.toString();
        }
    }

    /**
     * このマルチセットと別のオブジェクトの同値性を判定します。
     * 内部の各要素とその個数が一致する場合に同値とみなします。
     *
     * <p>計算量: $O(N)$($N$ は異なる要素の数)</p>
     *
     * @param obj
     * 		比較対象のオブジェクト
     * @return 同値であれば true, そうでなければ false
     */
    // 未テスト
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof IntTreapMultiSet)) {
            return false;
        }
        IntTreapMultiSet other = ((IntTreapMultiSet) (obj));
        if (this.size != other.size) {
            return false;
        }
        if (this.numberDistinctElements() != other.numberDistinctElements()) {
            return false;
        }
        List<Entry> thisEntries = this.entryList();
        List<Entry> otherEntries = other.entryList();
        if (thisEntries.size() != otherEntries.size()) {
            return false;
        }
        for (int i = 0; i < thisEntries.size(); i++) {
            Entry e1 = thisEntries.get(i);
            Entry e2 = otherEntries.get(i);
            if ((e1.key != e2.key) || (e1.value != e2.value)) {
                return false;
            }
        }
        return true;
    }

    /**
     * このマルチセットのハッシュコードを計算します。
     *
     * <p>計算量: $O(N)$($N$ は異なる要素の数)</p>
     *
     * @return ハッシュコード
     */
    // 未テスト
    @Override
    public int hashCode() {
        int result = 1;
        result = (31 * result) + Long.hashCode(size);
        for (Entry e : entryList()) {
            result = (31 * result) + Integer.hashCode(e.key);
            result = (31 * result) + Long.hashCode(e.value);
        }
        return result;
    }
}

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;
    }

    public void printlnYesNo(boolean flag) {
        println(flag ? "Yes" : "No");
    }
}

/**
 * <ul>
 * <li>キーに関して二分探索木の順序を満たす</li>
 * <li>優先度(priority)に関して最大ヒープの性質を満たす</li>
 * </ul>
 */
class TreapMap {
    static final Random rnd = new Random();

    private int size = 0;

    public TreapMap() {
    }

    static class Node {
        long key;

        int priority;

        Node left;

        Node right;

        int size;

        long sum;

        long val;

        public Node(long key, long val) {
            this.key = key;
            this.val = val;
            this.priority = rnd.nextInt();
            recalc(this);
        }
    }

    private Node root;

    public static void recalc(Node t) {
        if (t == null) {
            return;
        }
        t.sum = t.val;
        t.size = 1;
        if (t.left != null) {
            t.sum += t.left.sum;
            t.size += t.left.size;
        }
        if (t.right != null) {
            t.sum += t.right.sum;
            t.size += t.right.size;
        }
    }

    /**
     * 右回転(Right Rotation)を行います。
     *
     * <p>次のように部分木を右回転させます:
     *
     * <pre>
     * y                x
     * / \     =>       / \
     * x   C            A   y
     * / \                  / \
     * A   B                B   C
     * </pre>
     *
     * @param y
     * 		回転対象となる部分木の根
     * @return 回転後の新しい部分木の根
     */
    private Node rotateRight(Node y) {
        Node x = y.left;
        y.left = x.right;
        x.right = y;
        recalc(y);
        recalc(x);
        return x;
    }

    /**
     * 左回転(Left Rotation)を行います。
     *
     * <p>次のように部分木を左回転させます:
     *
     * <pre>
     * x                   y
     * / \       =>        / \
     * A   y               x   C
     * / \             / \
     * B   C           A   B
     * </pre>
     *
     * @param x
     * 		回転対象となる部分木の根
     * @return 回転後の新しい部分木の根
     */
    private Node rotateLeft(Node x) {
        Node y = x.right;
        x.right = y.left;
        y.left = x;
        recalc(x);
        recalc(y);
        return y;
    }

    private Node put(Node node, long key, long val) {
        if (node == null) {
            ++size;
            return new Node(key, val);
        }
        if (key < node.key) {
            node.left = put(node.left, key, val);
            if (node.left.priority > node.priority) {
                node = rotateRight(node);
            }
        } else if (key > node.key) {
            node.right = put(node.right, key, val);
            if (node.right.priority > node.priority) {
                node = rotateLeft(node);
            }
        } else {
            node.val = val;
        }
        recalc(node);
        return node;
    }

    public void put(long key, long val) {
        root = put(root, key, val);
    }

    public long getOrDefault(long key, long defaultValue) {
        return getOrDefaultValue(getRoot(), key, defaultValue);
    }

    private long getOrDefaultValue(Node node, long key, long defaultValue) {
        if (node == null) {
            return defaultValue;
        }
        if (key == node.key) {
            return node.val;
        }
        return key < node.key ? getOrDefaultValue(node.left, key, defaultValue) : getOrDefaultValue(node.right, key, defaultValue);
    }

    /**
     * Treap から指定したキーを削除。
     *
     * <p>
     * 削除は次の手順で行われます:
     * <ol>
     * <li>二分探索木として削除対象ノードを見つける</li>
     * <li>優先度に応じて回転し、削除対象ノードを葉へ押し下げる</li>
     * <li>葉になったら削除する</li>
     * </ol>
     *
     * @param node
     * 		部分木の根
     * @param key
     * 		削除するキー
     * @return 更新後の部分木の根
    https://judge.yosupo.jp/submission/336359
     */
    private Node remove(Node node, long key) {
        if (node == null) {
            return null;
        }
        if (key < node.key) {
            node.left = remove(node.left, key);
        } else if (key > node.key) {
            node.right = remove(node.right, key);
        } else if ((node.left == null) && (node.right == null)) {
            --size;
            return null;
        } else if (node.left == null) {
            node = rotateLeft(node);
            node.left = remove(node.left, key);
        } else if (node.right == null) {
            node = rotateRight(node);
            node.right = remove(node.right, key);
        } else if (node.left.priority > node.right.priority) {
            node = rotateRight(node);
            node.right = remove(node.right, key);
        } else {
            node = rotateLeft(node);
            node.left = remove(node.left, key);
        }
        recalc(node);
        return node;
    }

    public void remove(long key) {
        root = remove(getRoot(), key);
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public static class Entry {
        public final long key;

        public final long value;

        public Entry(long key, long value) {
            this.key = key;
            this.value = value;
        }
    }

    /**
     * https://atcoder.jp/contests/abc406/submissions/73198102
     *
     * @return  */
    public List<Entry> entryList() {
        List<Entry> res = new ArrayList<>();
        inOrderEntries(getRoot(), res);
        return res;
    }

    private void inOrderEntries(Node node, List<Entry> res) {
        if (node == null) {
            return;
        }
        inOrderEntries(node.left, res);
        res.add(new Entry(node.key, node.val));
        inOrderEntries(node.right, res);
    }

    public Node getRoot() {
        return root;
    }

    /**
     * このマップと指定されたオブジェクトが等価であるか検証します。
     *
     * @param o
     * 		比較対象のオブジェクト
     * @return 等価であれば true, そうでなければ false
    $O(N)$
    // 未テスト
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof TreapMap)) {
            return false;
        }
        TreapMap other = ((TreapMap) (o));
        if (this.size() != other.size()) {
            return false;
        }
        List<Entry> thisEntries = this.entryList();
        List<Entry> otherEntries = other.entryList();
        for (int i = 0; i < thisEntries.size(); i++) {
            Entry e1 = thisEntries.get(i);
            Entry e2 = otherEntries.get(i);
            if ((e1.key != e2.key) || (e1.value != e2.value)) {
                return false;
            }
        }
        return true;
    }

    /**
     * このマップのハッシュコード値を返します。
     *
     * @return ハッシュコード値
    $O(N)$
    // 未テスト
     */
    @Override
    public int hashCode() {
        int h = 0;
        List<Entry> entries = this.entryList();
        for (Entry entry : entries) {
            h += Long.hashCode(entry.key) ^ Long.hashCode(entry.value);
        }
        return h;
    }
}


// --- 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.collections.IntArrayList;
// import library.util.collections.IntTreapMultiSet;
// 
// 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 T=sc.nextInt();
// 		for (int TEST = 0; TEST < T; TEST++) {
// 			int N=sc.nextInt();
// 			int[]X=new int[2*N];
// 			int[]Y=new int[2*N];
// 			boolean[]F=new boolean[2*N];
// 			for (int i = 0; i < 2*N; i++) {
// 				X[i]=sc.nextInt();
// 				Y[i]=sc.nextInt();
// 				F[i]=sc.next().charAt(0)=='x';
// 			}
// 			IntTreapMultiSet set0=new IntTreapMultiSet();
// 			IntTreapMultiSet set1=new IntTreapMultiSet();
// 			for (int i = 0; i < 2*N; i++) {
// 				if(F[i]) {
// 					set0.add(Y[i]);
// 				} else {
// 					set1.add(X[i]);
// 				}
// 			}
// 			int c0=0;
// 			int c1=0;
// 			IntArrayList list0=new IntArrayList();
// 			IntArrayList list1=new IntArrayList();
// 			for (var es : set0.entryList()) {
// 				list0.add((int)es.value);
// 			}
// 			for (var es : set1.entryList()) {
// 				list1.add((int)es.value);
// 			}
// 			for (int i = 0; i < list0.size(); i++) {
// 				if(list0.get(i)%2==1) {
// 					list0.set(i, list0.get(i)-1);
// 					c0++;
// 				}
// 			}
// 			for (int i = 0; i < list1.size(); i++) {
// 				if(list1.get(i)%2==1) {
// 					list1.set(i, list1.get(i)-1);
// 					c1++;
// 				}
// 			}
// 			int m=Math.min(c0, c1);
// 			c0-=m;
// 			c1-=m;
// 			if (c0 > 0) {
// 				pw.printlnYesNo(ArrayUtils.sum(list1.toArray()) >= c0);
// 			} else if (c1 > 0) {
// 				pw.printlnYesNo(ArrayUtils.sum(list0.toArray()) >= c1);
// 			} else {
// 				pw.println("Yes");
// 			}
//  		}
// 	}
// 	
// 	void tr(Object... objects) {
// 		System.out.println(Arrays.deepToString(objects));
// 	}
// }
// 
0