QOJ.ac
QOJ
ID | Problem | Submitter | Result | Time | Memory | Language | File size | Submit time | Judge time |
---|---|---|---|---|---|---|---|---|---|
#106763 | #5670. Group Guests | maspy | TL | 2ms | 3576kb | C++23 | 33.6kb | 2023-05-19 05:11:22 | 2023-05-19 05:11:24 |
Judging History
answer
#line 1 "library/my_template.hpp"
#if defined(LOCAL)
#include <my_template_compiled.hpp>
#else
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using u32 = unsigned int;
using u64 = unsigned long long;
using i128 = __int128;
template <class T>
constexpr T infty = 0;
template <>
constexpr int infty<int> = 1'000'000'000;
template <>
constexpr ll infty<ll> = ll(infty<int>) * infty<int> * 2;
template <>
constexpr u32 infty<u32> = infty<int>;
template <>
constexpr u64 infty<u64> = infty<ll>;
template <>
constexpr i128 infty<i128> = i128(infty<ll>) * infty<ll>;
template <>
constexpr double infty<double> = infty<ll>;
template <>
constexpr long double infty<long double> = infty<ll>;
using pi = pair<ll, ll>;
using vi = vector<ll>;
template <class T>
using vc = vector<T>;
template <class T>
using vvc = vector<vc<T>>;
template <class T>
using vvvc = vector<vvc<T>>;
template <class T>
using vvvvc = vector<vvvc<T>>;
template <class T>
using vvvvvc = vector<vvvvc<T>>;
template <class T>
using pq = priority_queue<T>;
template <class T>
using pqg = priority_queue<T, vector<T>, greater<T>>;
#define vv(type, name, h, ...) \
vector<vector<type>> name(h, vector<type>(__VA_ARGS__))
#define vvv(type, name, h, w, ...) \
vector<vector<vector<type>>> name( \
h, vector<vector<type>>(w, vector<type>(__VA_ARGS__)))
#define vvvv(type, name, a, b, c, ...) \
vector<vector<vector<vector<type>>>> name( \
a, vector<vector<vector<type>>>( \
b, vector<vector<type>>(c, vector<type>(__VA_ARGS__))))
// https://trap.jp/post/1224/
#define FOR1(a) for (ll _ = 0; _ < ll(a); ++_)
#define FOR2(i, a) for (ll i = 0; i < ll(a); ++i)
#define FOR3(i, a, b) for (ll i = a; i < ll(b); ++i)
#define FOR4(i, a, b, c) for (ll i = a; i < ll(b); i += (c))
#define FOR1_R(a) for (ll i = (a)-1; i >= ll(0); --i)
#define FOR2_R(i, a) for (ll i = (a)-1; i >= ll(0); --i)
#define FOR3_R(i, a, b) for (ll i = (b)-1; i >= ll(a); --i)
#define overload4(a, b, c, d, e, ...) e
#define overload3(a, b, c, d, ...) d
#define FOR(...) overload4(__VA_ARGS__, FOR4, FOR3, FOR2, FOR1)(__VA_ARGS__)
#define FOR_R(...) overload3(__VA_ARGS__, FOR3_R, FOR2_R, FOR1_R)(__VA_ARGS__)
#define FOR_subset(t, s) \
for (ll t = (s); t >= 0; t = (t == 0 ? -1 : (t - 1) & (s)))
#define all(x) x.begin(), x.end()
#define len(x) ll(x.size())
#define elif else if
#define eb emplace_back
#define mp make_pair
#define mt make_tuple
#define fi first
#define se second
#define stoi stoll
int popcnt(int x) { return __builtin_popcount(x); }
int popcnt(u32 x) { return __builtin_popcount(x); }
int popcnt(ll x) { return __builtin_popcountll(x); }
int popcnt(u64 x) { return __builtin_popcountll(x); }
// (0, 1, 2, 3, 4) -> (-1, 0, 1, 1, 2)
int topbit(int x) { return (x == 0 ? -1 : 31 - __builtin_clz(x)); }
int topbit(u32 x) { return (x == 0 ? -1 : 31 - __builtin_clz(x)); }
int topbit(ll x) { return (x == 0 ? -1 : 63 - __builtin_clzll(x)); }
int topbit(u64 x) { return (x == 0 ? -1 : 63 - __builtin_clzll(x)); }
// (0, 1, 2, 3, 4) -> (-1, 0, 1, 0, 2)
int lowbit(int x) { return (x == 0 ? -1 : __builtin_ctz(x)); }
int lowbit(u32 x) { return (x == 0 ? -1 : __builtin_ctz(x)); }
int lowbit(ll x) { return (x == 0 ? -1 : __builtin_ctzll(x)); }
int lowbit(u64 x) { return (x == 0 ? -1 : __builtin_ctzll(x)); }
template <typename T, typename U>
T ceil(T x, U y) {
return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U>
T floor(T x, U y) {
return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T, typename U>
pair<T, T> divmod(T x, U y) {
T q = floor(x, y);
return {q, x - q * y};
}
template <typename T, typename U>
T SUM(const vector<U> &A) {
T sum = 0;
for (auto &&a: A) sum += a;
return sum;
}
#define MIN(v) *min_element(all(v))
#define MAX(v) *max_element(all(v))
#define LB(c, x) distance((c).begin(), lower_bound(all(c), (x)))
#define UB(c, x) distance((c).begin(), upper_bound(all(c), (x)))
#define UNIQUE(x) \
sort(all(x)), x.erase(unique(all(x)), x.end()), x.shrink_to_fit()
template <typename T>
T POP(deque<T> &que) {
T a = que.front();
que.pop_front();
return a;
}
template <typename T>
T POP(pq<T> &que) {
T a = que.top();
que.pop();
return a;
}
template <typename T>
T POP(pqg<T> &que) {
assert(!que.empty());
T a = que.top();
que.pop();
return a;
}
template <typename T>
T POP(vc<T> &que) {
assert(!que.empty());
T a = que.back();
que.pop_back();
return a;
}
template <typename F>
ll binary_search(F check, ll ok, ll ng, bool check_ok = true) {
if (check_ok) assert(check(ok));
while (abs(ok - ng) > 1) {
auto x = (ng + ok) / 2;
tie(ok, ng) = (check(x) ? mp(x, ng) : mp(ok, x));
}
return ok;
}
template <typename F>
double binary_search_real(F check, double ok, double ng, int iter = 100) {
FOR(iter) {
double x = (ok + ng) / 2;
tie(ok, ng) = (check(x) ? mp(x, ng) : mp(ok, x));
}
return (ok + ng) / 2;
}
template <class T, class S>
inline bool chmax(T &a, const S &b) {
return (a < b ? a = b, 1 : 0);
}
template <class T, class S>
inline bool chmin(T &a, const S &b) {
return (a > b ? a = b, 1 : 0);
}
// ? は -1
vc<int> s_to_vi(const string &S, char first_char) {
vc<int> A(S.size());
FOR(i, S.size()) { A[i] = (S[i] != '?' ? S[i] - first_char : -1); }
return A;
}
template <typename T, typename U>
vector<T> cumsum(vector<U> &A, int off = 1) {
int N = A.size();
vector<T> B(N + 1);
FOR(i, N) { B[i + 1] = B[i] + A[i]; }
if (off == 0) B.erase(B.begin());
return B;
}
// stable sort
template <typename T>
vector<int> argsort(const vector<T> &A) {
vector<int> ids(len(A));
iota(all(ids), 0);
sort(all(ids),
[&](int i, int j) { return (A[i] == A[j] ? i < j : A[i] < A[j]); });
return ids;
}
// A[I[0]], A[I[1]], ...
template <typename T>
vc<T> rearrange(const vc<T> &A, const vc<int> &I) {
vc<T> B(len(I));
FOR(i, len(I)) B[i] = A[I[i]];
return B;
}
#endif
#line 1 "library/other/io.hpp"
// based on yosupo's fastio
#include <unistd.h>
namespace fastio {
#define FASTIO
// クラスが read(), print() を持っているかを判定するメタ関数
struct has_write_impl {
template <class T>
static auto check(T &&x) -> decltype(x.write(), std::true_type{});
template <class T>
static auto check(...) -> std::false_type;
};
template <class T>
class has_write : public decltype(has_write_impl::check<T>(std::declval<T>())) {
};
struct has_read_impl {
template <class T>
static auto check(T &&x) -> decltype(x.read(), std::true_type{});
template <class T>
static auto check(...) -> std::false_type;
};
template <class T>
class has_read : public decltype(has_read_impl::check<T>(std::declval<T>())) {};
struct Scanner {
FILE *fp;
char line[(1 << 15) + 1];
size_t st = 0, ed = 0;
void reread() {
memmove(line, line + st, ed - st);
ed -= st;
st = 0;
ed += fread(line + ed, 1, (1 << 15) - ed, fp);
line[ed] = '\0';
}
bool succ() {
while (true) {
if (st == ed) {
reread();
if (st == ed) return false;
}
while (st != ed && isspace(line[st])) st++;
if (st != ed) break;
}
if (ed - st <= 50) {
bool sep = false;
for (size_t i = st; i < ed; i++) {
if (isspace(line[i])) {
sep = true;
break;
}
}
if (!sep) reread();
}
return true;
}
template <class T, enable_if_t<is_same<T, string>::value, int> = 0>
bool read_single(T &ref) {
if (!succ()) return false;
while (true) {
size_t sz = 0;
while (st + sz < ed && !isspace(line[st + sz])) sz++;
ref.append(line + st, sz);
st += sz;
if (!sz || st != ed) break;
reread();
}
return true;
}
template <class T, enable_if_t<is_integral<T>::value, int> = 0>
bool read_single(T &ref) {
if (!succ()) return false;
bool neg = false;
if (line[st] == '-') {
neg = true;
st++;
}
ref = T(0);
while (isdigit(line[st])) { ref = 10 * ref + (line[st++] & 0xf); }
if (neg) ref = -ref;
return true;
}
template <typename T,
typename enable_if<has_read<T>::value>::type * = nullptr>
inline bool read_single(T &x) {
x.read();
return true;
}
bool read_single(double &ref) {
string s;
if (!read_single(s)) return false;
ref = std::stod(s);
return true;
}
bool read_single(char &ref) {
string s;
if (!read_single(s) || s.size() != 1) return false;
ref = s[0];
return true;
}
template <class T>
bool read_single(vector<T> &ref) {
for (auto &d: ref) {
if (!read_single(d)) return false;
}
return true;
}
template <class T, class U>
bool read_single(pair<T, U> &p) {
return (read_single(p.first) && read_single(p.second));
}
template <size_t N = 0, typename T>
void read_single_tuple(T &t) {
if constexpr (N < std::tuple_size<T>::value) {
auto &x = std::get<N>(t);
read_single(x);
read_single_tuple<N + 1>(t);
}
}
template <class... T>
bool read_single(tuple<T...> &tpl) {
read_single_tuple(tpl);
return true;
}
void read() {}
template <class H, class... T>
void read(H &h, T &... t) {
bool f = read_single(h);
assert(f);
read(t...);
}
Scanner(FILE *fp) : fp(fp) {}
};
struct Printer {
Printer(FILE *_fp) : fp(_fp) {}
~Printer() { flush(); }
static constexpr size_t SIZE = 1 << 15;
FILE *fp;
char line[SIZE], small[50];
size_t pos = 0;
void flush() {
fwrite(line, 1, pos, fp);
pos = 0;
}
void write(const char val) {
if (pos == SIZE) flush();
line[pos++] = val;
}
template <class T, enable_if_t<is_integral<T>::value, int> = 0>
void write(T val) {
if (pos > (1 << 15) - 50) flush();
if (val == 0) {
write('0');
return;
}
if (val < 0) {
write('-');
val = -val; // todo min
}
size_t len = 0;
while (val) {
small[len++] = char(0x30 | (val % 10));
val /= 10;
}
for (size_t i = 0; i < len; i++) { line[pos + i] = small[len - 1 - i]; }
pos += len;
}
void write(const string s) {
for (char c: s) write(c);
}
void write(const char *s) {
size_t len = strlen(s);
for (size_t i = 0; i < len; i++) write(s[i]);
}
void write(const double x) {
ostringstream oss;
oss << fixed << setprecision(15) << x;
string s = oss.str();
write(s);
}
void write(const long double x) {
ostringstream oss;
oss << fixed << setprecision(15) << x;
string s = oss.str();
write(s);
}
template <typename T,
typename enable_if<has_write<T>::value>::type * = nullptr>
inline void write(T x) {
x.write();
}
template <class T>
void write(const vector<T> val) {
auto n = val.size();
for (size_t i = 0; i < n; i++) {
if (i) write(' ');
write(val[i]);
}
}
template <class T, class U>
void write(const pair<T, U> val) {
write(val.first);
write(' ');
write(val.second);
}
template <size_t N = 0, typename T>
void write_tuple(const T t) {
if constexpr (N < std::tuple_size<T>::value) {
if constexpr (N > 0) { write(' '); }
const auto x = std::get<N>(t);
write(x);
write_tuple<N + 1>(t);
}
}
template <class... T>
bool write(tuple<T...> tpl) {
write_tuple(tpl);
return true;
}
template <class T, size_t S>
void write(const array<T, S> val) {
auto n = val.size();
for (size_t i = 0; i < n; i++) {
if (i) write(' ');
write(val[i]);
}
}
void write(i128 val) {
string s;
bool negative = 0;
if (val < 0) {
negative = 1;
val = -val;
}
while (val) {
s += '0' + int(val % 10);
val /= 10;
}
if (negative) s += "-";
reverse(all(s));
if (len(s) == 0) s = "0";
write(s);
}
};
Scanner scanner = Scanner(stdin);
Printer printer = Printer(stdout);
void flush() { printer.flush(); }
void print() { printer.write('\n'); }
template <class Head, class... Tail>
void print(Head &&head, Tail &&... tail) {
printer.write(head);
if (sizeof...(Tail)) printer.write(' ');
print(forward<Tail>(tail)...);
}
void read() {}
template <class Head, class... Tail>
void read(Head &head, Tail &... tail) {
scanner.read(head);
read(tail...);
}
} // namespace fastio
using fastio::print;
using fastio::flush;
using fastio::read;
#define INT(...) \
int __VA_ARGS__; \
read(__VA_ARGS__)
#define LL(...) \
ll __VA_ARGS__; \
read(__VA_ARGS__)
#define STR(...) \
string __VA_ARGS__; \
read(__VA_ARGS__)
#define CHAR(...) \
char __VA_ARGS__; \
read(__VA_ARGS__)
#define DBL(...) \
double __VA_ARGS__; \
read(__VA_ARGS__)
#define VEC(type, name, size) \
vector<type> name(size); \
read(name)
#define VV(type, name, h, w) \
vector<vector<type>> name(h, vector<type>(w)); \
read(name)
void YES(bool t = 1) { print(t ? "YES" : "NO"); }
void NO(bool t = 1) { YES(!t); }
void Yes(bool t = 1) { print(t ? "Yes" : "No"); }
void No(bool t = 1) { Yes(!t); }
void yes(bool t = 1) { print(t ? "yes" : "no"); }
void no(bool t = 1) { yes(!t); }
#line 3 "main.cpp"
#line 2 "library/graph/base.hpp"
template <typename T>
struct Edge {
int frm, to;
T cost;
int id;
};
template <typename T = int, bool directed = false>
struct Graph {
int N, M;
using cost_type = T;
using edge_type = Edge<T>;
vector<edge_type> edges;
vector<int> indptr;
vector<edge_type> csr_edges;
vc<int> vc_deg, vc_indeg, vc_outdeg;
bool prepared;
class OutgoingEdges {
public:
OutgoingEdges(const Graph* G, int l, int r) : G(G), l(l), r(r) {}
const edge_type* begin() const {
if (l == r) { return 0; }
return &G->csr_edges[l];
}
const edge_type* end() const {
if (l == r) { return 0; }
return &G->csr_edges[r];
}
private:
const Graph* G;
int l, r;
};
bool is_prepared() { return prepared; }
constexpr bool is_directed() { return directed; }
Graph() : N(0), M(0), prepared(0) {}
Graph(int N) : N(N), M(0), prepared(0) {}
void build(int n) {
N = n, M = 0;
prepared = 0;
edges.clear();
indptr.clear();
csr_edges.clear();
vc_deg.clear();
vc_indeg.clear();
vc_outdeg.clear();
}
void add(int frm, int to, T cost = 1, int i = -1) {
assert(!prepared);
assert(0 <= frm && 0 <= to && to < N);
if (i == -1) i = M;
auto e = edge_type({frm, to, cost, i});
edges.eb(e);
++M;
}
// wt, off
void read_tree(bool wt = false, int off = 1) { read_graph(N - 1, wt, off); }
void read_graph(int M, bool wt = false, int off = 1) {
for (int m = 0; m < M; ++m) {
INT(a, b);
a -= off, b -= off;
if (!wt) {
add(a, b);
} else {
T c;
read(c);
add(a, b, c);
}
}
build();
}
void build() {
assert(!prepared);
prepared = true;
indptr.assign(N + 1, 0);
for (auto&& e: edges) {
indptr[e.frm + 1]++;
if (!directed) indptr[e.to + 1]++;
}
for (int v = 0; v < N; ++v) { indptr[v + 1] += indptr[v]; }
auto counter = indptr;
csr_edges.resize(indptr.back() + 1);
for (auto&& e: edges) {
csr_edges[counter[e.frm]++] = e;
if (!directed)
csr_edges[counter[e.to]++] = edge_type({e.to, e.frm, e.cost, e.id});
}
}
OutgoingEdges operator[](int v) const {
assert(prepared);
return {this, indptr[v], indptr[v + 1]};
}
vc<int> deg_array() {
if (vc_deg.empty()) calc_deg();
return vc_deg;
}
pair<vc<int>, vc<int>> deg_array_inout() {
if (vc_indeg.empty()) calc_deg_inout();
return {vc_indeg, vc_outdeg};
}
int deg(int v) {
if (vc_deg.empty()) calc_deg();
return vc_deg[v];
}
int in_deg(int v) {
if (vc_indeg.empty()) calc_deg_inout();
return vc_indeg[v];
}
int out_deg(int v) {
if (vc_outdeg.empty()) calc_deg_inout();
return vc_outdeg[v];
}
void debug() {
print("Graph");
if (!prepared) {
print("frm to cost id");
for (auto&& e: edges) print(e.frm, e.to, e.cost, e.id);
} else {
print("indptr", indptr);
print("frm to cost id");
FOR(v, N) for (auto&& e: (*this)[v]) print(e.frm, e.to, e.cost, e.id);
}
}
// G における頂点 V[i] が、新しいグラフで i になるようにする
Graph<T, directed> rearrange(vc<int> V) {
int n = len(V);
map<int, int> MP;
FOR(i, n) MP[V[i]] = i;
Graph<T, directed> G(n);
for (auto&& e: edges) {
if (MP.count(e.frm) && MP.count(e.to)) {
G.add(MP[e.frm], MP[e.to], e.cost);
}
}
G.build();
return G;
}
private:
void calc_deg() {
assert(vc_deg.empty());
vc_deg.resize(N);
for (auto&& e: edges) vc_deg[e.frm]++, vc_deg[e.to]++;
}
void calc_deg_inout() {
assert(vc_indeg.empty());
vc_indeg.resize(N);
vc_outdeg.resize(N);
for (auto&& e: edges) { vc_indeg[e.to]++, vc_outdeg[e.frm]++; }
}
};
#line 2 "library/graph/block_cut.hpp"
/*
block-cut tree を、block に通常の頂点を隣接させて拡張しておく
https://twitter.com/noshi91/status/1529858538650374144?s=20&t=eznpFbuD9BDhfTb4PplFUg
[0, n):もとの頂点 [n, n + n_block):block
関節点:[0, n) のうちで、degree >= 2 を満たすもの
孤立点は、1 点だけからなる block
*/
template <typename GT>
Graph<int, 0> block_cut(GT& G) {
int n = G.N;
vc<int> low(n), ord(n), st;
vc<bool> used(n);
st.reserve(n);
int nxt = n;
int k = 0;
vc<pair<int, int>> edges;
auto dfs = [&](auto& dfs, int v, int p) -> void {
st.eb(v);
used[v] = 1;
low[v] = ord[v] = k++;
int child = 0;
for (auto&& e: G[v]) {
if (e.to == p) continue;
if (!used[e.to]) {
++child;
int s = len(st);
dfs(dfs, e.to, v);
chmin(low[v], low[e.to]);
if ((p == -1 && child > 1) || (p != -1 && low[e.to] >= ord[v])) {
edges.eb(nxt, v);
while (len(st) > s) {
edges.eb(nxt, st.back());
st.pop_back();
}
++nxt;
}
} else {
chmin(low[v], ord[e.to]);
}
}
};
FOR(v, n) if (!used[v]) {
dfs(dfs, v, -1);
for (auto&& x: st) { edges.eb(nxt, x); }
++nxt;
st.clear();
}
Graph<int, 0> BCT(nxt);
for (auto&& [a, b]: edges) BCT.add(a, b);
BCT.build();
return BCT;
}
#line 2 "library/graph/tree.hpp"
#line 4 "library/graph/tree.hpp"
// HLD euler tour をとっていろいろ。
// 木以外、非連結でも dfs 順序や親がとれる。
template <typename GT>
struct Tree {
using Graph_type = GT;
GT &G;
using WT = typename GT::cost_type;
int N;
vector<int> LID, RID, head, V, parent, VtoE;
vc<int> depth;
vc<WT> depth_weighted;
Tree(GT &G, int r = 0, bool hld = 1) : G(G) { build(r, hld); }
void build(int r = 0, bool hld = 1) {
if (r == -1) return; // build を遅延したいとき
N = G.N;
LID.assign(N, -1), RID.assign(N, -1), head.assign(N, r);
V.assign(N, -1), parent.assign(N, -1), VtoE.assign(N, -1);
depth.assign(N, -1), depth_weighted.assign(N, 0);
assert(G.is_prepared());
int t1 = 0;
dfs_sz(r, -1, hld);
dfs_hld(r, t1);
}
void dfs_sz(int v, int p, bool hld) {
auto &sz = RID;
parent[v] = p;
depth[v] = (p == -1 ? 0 : depth[p] + 1);
sz[v] = 1;
int l = G.indptr[v], r = G.indptr[v + 1];
auto &csr = G.csr_edges;
// 使う辺があれば先頭にする
for (int i = r - 2; i >= l; --i) {
if (hld && depth[csr[i + 1].to] == -1) swap(csr[i], csr[i + 1]);
}
int hld_sz = 0;
for (int i = l; i < r; ++i) {
auto e = csr[i];
if (depth[e.to] != -1) continue;
depth_weighted[e.to] = depth_weighted[v] + e.cost;
VtoE[e.to] = e.id;
dfs_sz(e.to, v, hld);
sz[v] += sz[e.to];
if (hld && chmax(hld_sz, sz[e.to]) && l < i) { swap(csr[l], csr[i]); }
}
}
void dfs_hld(int v, int ×) {
LID[v] = times++;
RID[v] += LID[v];
V[LID[v]] = v;
bool heavy = true;
for (auto &&e: G[v]) {
if (depth[e.to] <= depth[v]) continue;
head[e.to] = (heavy ? head[v] : e.to);
heavy = false;
dfs_hld(e.to, times);
}
}
vc<int> heavy_path_at(int v) {
vc<int> P = {v};
while (1) {
int a = P.back();
for (auto &&e: G[a]) {
if (e.to != parent[a] && head[e.to] == v) {
P.eb(e.to);
break;
}
}
if (P.back() == a) break;
}
return P;
}
int e_to_v(int eid) {
auto e = G.edges[eid];
return (parent[e.frm] == e.to ? e.frm : e.to);
}
int v_to_e(int v) { return VtoE[v]; }
int ELID(int v) { return 2 * LID[v] - depth[v]; }
int ERID(int v) { return 2 * RID[v] - depth[v] - 1; }
/* k: 0-indexed */
int LA(int v, int k) {
assert(k <= depth[v]);
while (1) {
int u = head[v];
if (LID[v] - k >= LID[u]) return V[LID[v] - k];
k -= LID[v] - LID[u] + 1;
v = parent[u];
}
}
int la(int u, int v) { return LA(u, v); }
int LCA(int u, int v) {
for (;; v = parent[head[v]]) {
if (LID[u] > LID[v]) swap(u, v);
if (head[u] == head[v]) return u;
}
}
// root を根とした場合の lca
int LCA_root(int u, int v, int root) {
return LCA(u, v) ^ LCA(u, root) ^ LCA(v, root);
}
int lca(int u, int v) { return LCA(u, v); }
int lca_root(int u, int v, int root) { return LCA_root(u, v, root); }
int subtree_size(int v, int root = -1) {
if (root == -1) return RID[v] - LID[v];
if (v == root) return N;
int x = jump(v, root, 1);
if (in_subtree(v, x)) return RID[v] - LID[v];
return N - RID[x] + LID[x];
}
int dist(int a, int b) {
int c = LCA(a, b);
return depth[a] + depth[b] - 2 * depth[c];
}
WT dist_weighted(int a, int b) {
int c = LCA(a, b);
return depth_weighted[a] + depth_weighted[b] - WT(2) * depth_weighted[c];
}
// a is in b
bool in_subtree(int a, int b) { return LID[b] <= LID[a] && LID[a] < RID[b]; }
int jump(int a, int b, ll k) {
if (k == 1) {
if (a == b) return -1;
return (in_subtree(b, a) ? LA(b, depth[b] - depth[a] - 1) : parent[a]);
}
int c = LCA(a, b);
int d_ac = depth[a] - depth[c];
int d_bc = depth[b] - depth[c];
if (k > d_ac + d_bc) return -1;
if (k <= d_ac) return LA(a, k);
return LA(b, d_ac + d_bc - k);
}
vc<int> collect_child(int v) {
vc<int> res;
for (auto &&e: G[v])
if (e.to != parent[v]) res.eb(e.to);
return res;
}
vc<pair<int, int>> get_path_decomposition(int u, int v, bool edge) {
// [始点, 終点] の"閉"区間列。
vc<pair<int, int>> up, down;
while (1) {
if (head[u] == head[v]) break;
if (LID[u] < LID[v]) {
down.eb(LID[head[v]], LID[v]);
v = parent[head[v]];
} else {
up.eb(LID[u], LID[head[u]]);
u = parent[head[u]];
}
}
if (LID[u] < LID[v]) down.eb(LID[u] + edge, LID[v]);
elif (LID[v] + edge <= LID[u]) up.eb(LID[u], LID[v] + edge);
reverse(all(down));
up.insert(up.end(), all(down));
return up;
}
vc<int> restore_path(int u, int v) {
vc<int> P;
for (auto &&[a, b]: get_path_decomposition(u, v, 0)) {
if (a <= b) {
FOR(i, a, b + 1) P.eb(V[i]);
} else {
FOR_R(i, b, a + 1) P.eb(V[i]);
}
}
return P;
}
};
#line 2 "library/enumerate/triangle.hpp"
template <typename Gr, typename F>
void enumerate_triangle(Gr& G, F query) {
int N = G.N;
Graph<int, 1> H(N);
for (auto&& e: G.edges) {
// 注意:次数比較だけだと DAG にならず、サイクルができてしまう
if (mp(G.deg(e.frm), e.frm) < mp(G.deg(e.to), e.to))
H.add(e.frm, e.to);
else
H.add(e.to, e.frm);
}
H.build();
vc<bool> table(N);
FOR(a, N) {
for (auto&& e: H[a]) { table[e.to] = 1; }
for (auto&& e: H[a]) {
int b = e.to;
for (auto&& f: H[b]) {
int c = f.to;
if (table[c]) query(a, b, c);
}
}
for (auto&& e: H[a]) { table[e.to] = 0; }
}
}
#line 2 "library/ds/unionfind/unionfind.hpp"
struct UnionFind {
int n, n_comp;
vc<int> dat; // par or (-size)
UnionFind(int n = 0) { build(n); }
void build(int m) {
n = m, n_comp = m;
dat.assign(n, -1);
}
void reset() { build(n); }
int operator[](int x) {
while (dat[x] >= 0) {
int pp = dat[dat[x]];
if (pp < 0) { return dat[x]; }
x = dat[x] = pp;
}
return x;
}
ll size(int x) {
assert(dat[x] < 0);
return -dat[x];
}
bool merge(int x, int y) {
x = (*this)[x], y = (*this)[y];
if (x == y) return false;
if (-dat[x] < -dat[y]) swap(x, y);
dat[x] += dat[y], dat[y] = x, n_comp--;
return true;
}
};
#line 2 "library/ds/rollback_array.hpp"
template <typename T>
struct Rollback_Array {
int N;
vc<T> dat;
vc<pair<int, T>> history;
Rollback_Array(vc<T> x) : N(len(x)), dat(x) {}
Rollback_Array(int N) : N(N), dat(N) {}
template <typename F>
Rollback_Array(int N, F f) : N(N) {
dat.reserve(N);
FOR(i, N) dat.eb(f(i));
}
int time() { return len(history); }
void rollback(int t) {
FOR_R(i, t, time()) {
auto& [idx, v] = history[i];
dat[idx] = v;
}
history.resize(t);
}
T get(int idx) { return dat[idx]; }
void set(int idx, T x) {
history.eb(idx, dat[idx]);
dat[idx] = x;
}
vc<T> get_all() {
vc<T> res(N);
FOR(i, N) res[i] = get(i);
return res;
}
};
#line 2 "library/ds/unionfind/rollback_unionfind.hpp"
struct Rollback_UnionFind {
Rollback_Array<int> dat; // parent or size
Rollback_UnionFind(int n) : dat(vc<int>(n, -1)) {}
int operator[](int v) {
while (dat.get(v) >= 0) v = dat.get(v);
return v;
}
ll size(int v) { return -dat.get((*this)[v]); }
int time() { return dat.time(); }
void rollback(int t) { dat.rollback(t); }
bool merge(int a, int b) {
a = (*this)[a], b = (*this)[b];
if (a == b) return false;
if (dat.get(a) > dat.get(b)) swap(a, b);
dat.set(a, dat.get(a) + dat.get(b));
dat.set(b, a);
return true;
}
};
#line 1 "library/ds/offline_query/add_remove_query.hpp"
/*
・時刻 t に x を追加する
・時刻 t に x を削除する
があるときに、
・時刻 [l, r) に x を追加する
に変換する。
クエリが時系列順に来ることが分かっているときは monotone = true の方が高速。
*/
template <typename X, bool monotone>
struct Add_Remove_Query {
map<X, int> MP;
vc<tuple<int, int, X>> dat;
map<X, vc<int>> ADD;
map<X, vc<int>> RM;
void add(int time, X x) {
if (monotone) return add_monotone(time, x);
ADD[x].eb(time);
}
void remove(int time, X x) {
if (monotone) return remove_monotone(time, x);
RM[x].eb(time);
}
// すべてのクエリが終わった現在時刻を渡す
vc<tuple<int, int, X>> calc(int time) {
if (monotone) return calc_monotone(time);
vc<tuple<int, int, X>> dat;
for (auto&& [x, A]: ADD) {
vc<int> B;
if (RM.count(x)) {
B = RM[x];
RM.erase(x);
}
if (len(B) < len(A)) B.eb(time);
assert(len(A) == len(B));
sort(all(A));
sort(all(B));
FOR(i, len(A)) {
assert(A[i] <= B[i]);
if (A[i] < B[i]) dat.eb(A[i], B[i], x);
}
}
assert(len(RM) == 0);
return dat;
}
private:
void add_monotone(int time, X x) {
assert(!MP.count(x));
MP[x] = time;
}
void remove_monotone(int time, X x) {
auto it = MP.find(x);
assert(it != MP.end());
int t = (*it).se;
MP.erase(it);
if (t == time) return;
dat.eb(t, time, x);
}
vc<tuple<int, int, X>> calc_monotone(int time) {
for (auto&& [x, t]: MP) {
if (t == time) continue;
dat.eb(t, time, x);
}
return dat;
}
};
#line 12 "main.cpp"
bool triangle(Graph<bool, 0> G) {
ll N = G.N;
ll M = G.M;
// DFS tree
Graph<bool, 1> DFS(N);
{
vc<bool> vis(N);
auto dfs = [&](auto& dfs, int v, int p) -> void {
vis[v] = 1;
for (auto&& e: G[v]) {
if (e.to == p) continue;
if (vis[e.to]) continue;
DFS.add(v, e.to);
dfs(dfs, e.to, v);
}
};
dfs(dfs, 0, -1);
DFS.build();
assert(DFS.M == N - 1);
}
Tree<decltype(DFS)> tree(DFS);
auto in_tree = [&](int a, int b) -> bool {
return tree.parent[a] == b || tree.parent[b] == a;
};
bool OK = 0;
using T = tuple<int, int, int>;
vc<T> cand;
{
Graph<int, 1> H(N);
for (auto&& e: G.edges) {
// 注意:次数比較だけだと DAG にならず、サイクルができてしまう
if (mp(G.deg(e.frm), e.frm) < mp(G.deg(e.to), e.to))
H.add(e.frm, e.to);
else
H.add(e.to, e.frm);
}
H.build();
vc<bool> table(N);
FOR(a, N) {
for (auto&& e: H[a]) { table[e.to] = 1; }
for (auto&& e: H[a]) {
int b = e.to;
for (auto&& f: H[b]) {
int c = f.to;
if (table[c]) {
if (in_tree(a, b) || in_tree(b, c) || in_tree(c, a)) {
cand.eb(a, b, c);
} else {
return 1;
}
}
}
}
for (auto&& e: H[a]) { table[e.to] = 0; }
}
}
// それ以外の消し方は、高々 O(M) 通りである。
assert(len(cand) <= M);
return len(cand) > 0;
// あとは、rollback unionfind で頑張る
Add_Remove_Query<pair<int, int>, true> X;
int t = 0;
for (auto&& e: G.edges) {
int a = e.frm, b = e.to;
if (a > b) swap(a, b);
X.add(t, {a, b});
}
for (auto&& [a, b, c]: cand) {
if (a > b) swap(a, b);
if (b > c) swap(b, c);
if (a > b) swap(a, b);
if (b > c) swap(b, c);
if (a > b) swap(a, b);
if (b > c) swap(b, c);
X.remove(t, {a, b});
X.remove(t, {a, c});
X.remove(t, {b, c});
++t;
X.add(t, {a, b});
X.add(t, {a, c});
X.add(t, {b, c});
}
Rollback_UnionFind uf(N);
Rollback_Array<int> A(N); // root -> edge
int odd = 0;
// rollback_dfs
auto upd = X.calc(len(cand));
vc<int> I(len(upd));
iota(all(I), 0);
auto dfs = [&](auto& dfs, vc<int>& upd_query_I, int begin, int end) -> void {
if (OK) return;
if (begin == end) return;
// snapshot
int memo_odd = odd;
int uf_time = uf.time();
int A_time = A.time();
vc<int> IL, IR;
int mid = (begin + end) / 2;
for (auto&& i: upd_query_I) {
auto [a, b, X] = upd[i];
if (a <= begin && end <= b) {
// X で表される update query を処理する
auto [u, v] = X;
if (uf[u] != uf[v]) {
int a = uf[u], b = uf[v];
int xa = A.get(a), xb = A.get(b);
if (xa % 2 == 1) --odd;
if (xb % 2 == 1) --odd;
int x = A.get(a) + A.get(b);
if (x % 2 == 1) ++odd;
uf.merge(u, v);
a = uf[a];
A.set(a, x);
}
u = uf[u];
int x = A.get(u);
if (x % 2 == 1) --odd;
A.set(u, x + 1);
if (x % 2 == 0) ++odd;
} else {
if (a < mid) IL.eb(i);
if (mid < b) IR.eb(i);
}
}
if (begin + 1 == end) {
// 求値クエリ
if (odd == 0) OK = 1;
} else {
dfs(dfs, IL, begin, mid);
dfs(dfs, IR, mid, end);
}
// rollback
odd = memo_odd;
uf.rollback(uf_time);
A.rollback(A_time);
};
dfs(dfs, I, 0, len(cand));
return OK;
}
bool star(Graph<bool, 0> G) {
ll N = G.N;
ll M = G.M;
auto BCT = block_cut<decltype(G)>(G);
Tree<decltype(BCT)> tree(BCT);
vc<int> dp(BCT.N);
FOR(i, N) dp[i] = 1;
FOR_R(i, 1, BCT.N) {
int v = tree.V[i];
int p = tree.parent[v];
dp[p] += dp[v];
}
FOR(v, N) {
vc<int> nbd;
for (auto&& e: BCT[v]) nbd.eb(e.to);
sort(all(nbd));
int n = len(nbd);
vc<int> cnt(n);
for (auto&& e: G[v]) {
int idx = tree.jump(v, e.to, 1);
idx = LB(nbd, idx);
cnt[idx]++;
}
vc<int> parity(n);
FOR(i, n) {
if (nbd[i] != tree.parent[v]) {
parity[i] = dp[nbd[i]] & 1;
} else {
parity[i] = (N - dp[v]) & 1;
}
}
ll k = 0;
FOR(i, n) {
if (parity[i] == 0)
k += cnt[i];
else
k += cnt[i] - 1;
}
if (k >= 3) return true;
}
return false;
}
pi solve_connected(Graph<bool, 0> G) {
ll N = G.N;
ll M = G.M;
if (M % 2 == 0) return {0, 0};
if (triangle(G)) return {0, 0};
if (star(G)) return {0, 1};
return {1, 0};
}
void solve() {
LL(M, N);
Graph<bool, 0> G(N);
G.read_graph(M);
UnionFind uf(N);
for (auto&& e: G.edges) uf.merge(e.frm, e.to);
vvc<int> comp(N);
FOR(v, N) comp[uf[v]].eb(v);
pi ANS = {0, 0};
for (auto&& V: comp) {
if (len(V) <= 1) continue;
auto H = G.rearrange(V);
auto [a, b] = solve_connected(H);
ANS.fi += a, ANS.se += b;
}
print(ANS);
}
signed main() {
solve();
return 0;
}
Details
Tip: Click on the bar to expand more detailed information
Test #1:
score: 100
Accepted
time: 2ms
memory: 3428kb
input:
2 4 1 2 3 4
output:
2 0
result:
ok single line: '2 0'
Test #2:
score: 0
Accepted
time: 2ms
memory: 3576kb
input:
2 3 1 2 3 1
output:
0 0
result:
ok single line: '0 0'
Test #3:
score: 0
Accepted
time: 0ms
memory: 3424kb
input:
5 5 1 2 2 3 2 4 5 2 5 4
output:
0 0
result:
ok single line: '0 0'
Test #4:
score: -100
Time Limit Exceeded
input:
999990 666660 1 2 1 5 1 7 1 8 1 9 1 10 2 4 2 6 2 9 2 10 3 4 4 8 5 8 6 7 6 10 11 13 11 15 11 20 12 17 12 19 13 16 13 19 14 16 14 19 15 17 15 18 16 17 16 19 17 18 17 20 21 26 21 27 21 29 22 26 22 27 22 29 23 26 23 30 24 26 24 28 25 27 25 30 26 27 26 29 28 29 31 33 31 40 32 35 33 35 33 37 33 38 33 39 3...