#include using namespace std; using ll = long long; vector prime_table; void erat(ll N){ prime_table.resize(N+1, 1); prime_table[0] = 0; prime_table[1] = 0; for (ll i=2; i*i<=N; i++){ if (prime_table[i]){ for (ll j=i*2; j <= N; j+=i){ prime_table[j] = 0; } } } } vector> scc(const vector> &E){ int N = E.size(); vector> rev(N), components; vector vst(N); vector order; for (int i=0; ivoid{ vst[from] = 1; for (int to : E[from]){ if (vst[to]) continue; self(self, to); } order.push_back(from); }; for (int i=0; i &comp)->void{ vst[from] = 1; comp.push_back(from); for (int to : rev[from]){ if (vst[to]) continue; self(self, to, comp); } }; for (auto v : order){ if (!vst[v]){ vector comp; dfs2(dfs2, v, comp); reverse(comp.begin(), comp.end()); components.push_back(comp); } } return components; } //clause = [i1, i2, f1, f2] // x_i1 = f1 or x_i2 = f2 (f1 = 0なら !x_i1)のような2個のリテラルからなる節の論理積がtrueとなる割り当て(x1, x2, ..., xN)を求める。存在しないなら空の配列を返す。 vector two_sat(int N, const vector> &clause){ vector> E(N*2), sc; for (auto [i1, i2, f1, f2] : clause){ E[i1 * 2 + !f1].push_back(i2 * 2 + f2); E[i2 * 2 + !f2].push_back(i1 * 2 + f1); } sc = scc(E); int M = sc.size(); vector res(N); vector id(N*2); for (int i=0; i 2*i, B_i -> 2*i+1 (1) z_{i*2} or z_{i*2+1} (どちらかがS) (2) !z_{i*2} or !z_{i*2+1} (どちらかがT) (3) if (prime(i*2, j*2+1)) !z_{i*2} or z_{j*2+1} = !(z_{i*2} and !z_{j*2+1}) (SとTが素数にならない) */ erat(999999); int N; cin >> N; vector a(N), b(N); vector> v; for (int i=0; i> a[i] >> b[i]; for (int i=0; ibool{ int res = stoi(to_string(x) + to_string(y)); return prime_table[res]; }; for (int i=0; i ans = two_sat(N*2, v); cout << (ans.size() == 0 ? "No" : "Yes") << endl; return 0; }