Problem:
D: Two Colors Sort

問題

umg 君は散歩中に 1,2, ... ,N を並び変えてできる長さ N の数列 P_1, P_2, ..., P_N を見つけました。

umg 君は不思議な力を使うことで、同じ色に塗られた異なる数を二つ選んで場所を交換することができます。

umg 君は、数列に含まれる数のうち R 個を赤に、残りの N-R 個を青に塗ることで数列を昇順に並び替えられるようにしたいと考えました。

umg 君が目標を達成できるかどうか判定してください。

ただし、数字はとても重いので不思議な力を使わずに動かすことはできません。また、umg 君は天才なので不思議な力を任意の回数使うことができます。

入力形式

N R
P_1 P_2 ... P_N

制約

1 \leq N \leq 3 \times 10^5

1 \leq R \leq N

1\leq P_i \leq N

P_i \neq P_j (1 \leq i < j \leq N)

入力は全て整数である。

出力形式

umg 君が目的を達成できるなら Yes、そうでなければ No を一行に出力せよ。

入力例 1

3 2
1 3 2

出力例 1

Yes

1 を青に、2,3 を赤に塗ることで目標が達成できます。

入力例 2

5 2
1 2 3 4 5

出力例 2

Yes

初めから昇順に並んでいます。

入力例 3

10 7
3 4 8 5 7 6 2 10 1 9

出力例 3

No

Solution in JAVA (judge verdict: Accepted)

import java.util.ArrayList;
import java.util.Scanner;

public class Main {
	static Scanner sc = new Scanner(System.in);

	public static void main(String[] args) {
		int N = sc.nextInt();
		int R = sc.nextInt();
		R = Math.min(R, N - R);
		int[] P = new int[N];
		for (int i = 0; i < N; i++) {
			P[i] = Integer.parseInt(sc.next()) - 1;
		}
		int one = 0;
		ArrayList<Integer> len = new ArrayList<>();
		boolean[] used = new boolean[N];
		for (int i = 0; i < N; i++) {
			if (used[i]) continue;
			int cur = i;
			int l = 1;
			used[cur] = true;
			while (P[cur] != i) {
				cur = P[cur];
				++l;
				used[cur] = true;
			}
			if (l == 1) {
				one++;
			} else {
				len.add(l);
			}
		}
		Bits bs = new Bits(R + 1);
		bs.bits[0] = 1L;
		for (int i = 0; i < len.size(); i++) {
			bs.shiftLOr(len.get(i));
		}
		boolean ans = false;
		for (int i = 0; i <= one && i <= R; i++) {
			if (bs.get(R - i)) {
				ans = true;
				break;
			}
		}
		System.out.println(ans ? "Yes" : "No");

	}

	static class Bits {
		long[] bits;

		Bits(int size) {
			bits = new long[(size + 63) / 64];
		}

		void shiftLOr(int shift) {
			int m1 = shift >> 6;
			int m2 = shift & 63;
			if (m2 == 0) {
				for (int i = bits.length - 1 - m1; i >= 0; i--) {
					bits[i + m1] |= bits[i];
				}
			} else {
				for (int i = bits.length - 1 - m1; i >= 0; i--) {
					int p1 = i + m1 + 1;
					int p2 = i + m1;
					long v1 = bits[i] >>> (64 - m2);
					long v2 = bits[i] << m2;
					if (p1 < bits.length) {
						bits[p1] |= v1;
					}
					bits[p2] |= v2;
				}
			}
		}

		boolean get(int pos) {
			return ((bits[pos >> 6] >>> (pos & 63)) & 1) != 0;
		}

		void set(int pos) {
			bits[pos >> 6] |= (1L << (pos & 63));
		}
	}

}