**ICPC NOTEBOOK** **School : Le Hong Phong High School for The Gifted** **Team : No_Zone_for_Loser** **Member : Nguyễn Tấn Minh (11CTIN-A), Đặng Thành Nghĩa (11CTIN-A), Lê Lâm (10CTIN)** [TOC] # Main template ```cpp! #include<bits/stdc++.h> using namespace std; #define range(v) begin(v), end(v) #define compact(v) v.erase(unique(range(v)), end(v)) #define len(x) (int)(x).size() #define BIT(x, i) ((x)>>(i)&1) #define MASK(x) (1LL<<(x)) #define forw(i,j,z) for(int i=(int)j;i<=(int)z;i++) #define ford(i,j,z) for(int i=(int)j;i>=(int)z;i--) #define mp make_pair //define lucian nghiaaa's cuties //define zero_op nghiaaa's cuties //define No_Zone_for_Loser winner team typedef long long ll; typedef long double ld; typedef unsigned long long ull; template<class T> bool maximize(T& a, const T& b){ if(a<b) return a=b, true; return false; } template<class T> bool minimize(T& a, const T& b){ if(a>b) return a=b, true; return false; } void No_Zone_for_Loser(){ } signed main(){ ios_base::sync_with_stdio(0); cin.tie(NULL); cout.tie(NULL); #define task "a" if(fopen(task".inp", "r")){ freopen(task".inp", "r", stdin); freopen(task".out", "w", stdout); } int TC=1; //cin>>TC; while(TC--) No_Zone_for_Loser(); return 0; } ``` # MATH ## Miller Rabin ```cpp! // From https://github.com/SnapDragon64/ContestLibrary/blob/master/math.h // which also has specialized versions for 32-bit and 42-bit // // Tested: // - https://oj.vnoi.info/problem/icpc22_national_c (fastest solution) // - https://www.spoj.com/problems/PON/ // Rabin miller {{{ inline uint64_t mod_mult64(uint64_t a, uint64_t b, uint64_t m) { return __int128_t(a) * b % m; } uint64_t mod_pow64(uint64_t a, uint64_t b, uint64_t m) { uint64_t ret = (m > 1); for (;;) { if (b & 1) ret = mod_mult64(ret, a, m); if (!(b >>= 1)) return ret; a = mod_mult64(a, a, m); } } // Works for all primes p < 2^64 bool is_prime(uint64_t n) { if (n <= 3) return (n >= 2); static const uint64_t small[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, }; for (size_t i = 0; i < sizeof(small) / sizeof(uint64_t); ++i) { if (n % small[i] == 0) return n == small[i]; } // Makes use of the known bounds for Miller-Rabin pseudoprimes. static const uint64_t millerrabin[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, }; static const uint64_t A014233[] = { // From OEIS. 2047LL, 1373653LL, 25326001LL, 3215031751LL, 2152302898747LL, 3474749660383LL, 341550071728321LL, 341550071728321LL, 3825123056546413051LL, 3825123056546413051LL, 3825123056546413051LL, 0, }; uint64_t s = n-1, r = 0; while (s % 2 == 0) { s /= 2; r++; } for (size_t i = 0, j; i < sizeof(millerrabin) / sizeof(uint64_t); i++) { uint64_t md = mod_pow64(millerrabin[i], s, n); if (md != 1) { for (j = 1; j < r; j++) { if (md == n-1) break; md = mod_mult64(md, md, n); } if (md != n-1) return false; } if (n < A014233[i]) return true; } return true; } // }}} ``` ## FFT ```cpp! using cd=complex<double>; const double PI=acos(-1); void fft(vector<cd>& a, bool is_inverse){ int n=a.size(); int lg=0; while((1<<lg)<n) ++lg; for(int i=1, j=0; i<n; ++i){ int bit=n>>1; for(; j&bit; bit>>=1) j^=bit; j^=bit; if(i<j) swap(a[i], a[j]); } for(int len=2; len<=n; len<<=1){ double ang=2.0*PI/len*(is_inverse?-1:1); cd wlen(cos(ang), sin(ang)); for(int i=0; i<n; i+=len){ cd w(1); for(int j=0; j*2<len; ++j){ cd u=a[i+j], v=a[i+j+len/2]*w; a[i+j]=u+v; a[i+j+len/2]=u-v; w*=wlen; } } } if(is_inverse){ for(cd &x:a) x/=n; } } vector<int> multiply(const vector<int>& a, const vector<int>& b){ vector<cd> fa(all(a)), fb(all(b)); int n=1; while(a.size()+b.size()>n) n<<=1; fa.resize(n), fb.resize(n); fft(fa, 0), fft(fb, 0); for(int i=0; i<n; ++i) fa[i]*=fb[i]; fft(fa, 1); vector<int> result(n); for(int i=0; i<n; ++i) result[i]=round(fa[i].real()); return result; } ``` ## NTT ```cpp! // NTT {{{ // // Faster than NTT_chemthan.h // // Usage: // auto c = multiply(a, b); // where a and b are vector<ModInt<ANY_MOD>> // (If mod is NOT NTT_PRIMES, it does 3 NTT and combine result) constexpr int NTT_PRIMES[] = {998244353, 167772161, 469762049}; // assumptions: // - |a| is power of 2 // - mint::mod() is a valid NTT primes (2^k * m + 1) template<typename mint> void ntt(std::vector<mint>& a, bool is_inverse) { int n = a.size(); if (n == 1) return; static const int mod = mint::mod(); static const mint root = mint::get_primitive_root(); assert(__builtin_popcount(n) == 1 && (mod - 1) % n == 0); static std::vector<mint> w{1}, iw{1}; for (int m = w.size(); m < n / 2; m *= 2) { mint dw = root.pow((mod - 1) / (4 * m)); mint dwinv = dw.inv(); w.resize(m * 2); iw.resize(m * 2); for (int i = 0; i < m; ++i) { w[m + i] = w[i] * dw; iw[m + i] = iw[i] * dwinv; } } if (!is_inverse) { for (int m = n; m >>= 1; ) { for (int s = 0, k = 0; s < n; s += 2 * m, ++k) { for (int i = s; i < s + m; ++i) { mint x = a[i], y = a[i + m] * w[k]; a[i] = x + y; a[i + m] = x - y; } } } } else { for (int m = 1; m < n; m *= 2) { for (int s = 0, k = 0; s < n; s += 2 * m, ++k) { for (int i = s; i < s + m; ++i) { mint x = a[i], y = a[i + m]; a[i] = x + y; a[i + m] = (x - y) * iw[k]; } } } int n_inv = mint(n).inv().x; for (auto& v : a) v *= n_inv; } } template<typename mint> std::vector<mint> ntt_multiply(int sz, std::vector<mint> a, std::vector<mint> b) { a.resize(sz); b.resize(sz); if (a == b) { // optimization for squaring polynomial ntt(a, false); b = a; } else { ntt(a, false); ntt(b, false); } for (int i = 0; i < sz; ++i) a[i] *= b[i]; ntt(a, true); return a; } template<int MOD, typename mint> std::vector<ModInt<MOD>> convert_mint_and_multiply( int sz, const std::vector<mint>& a, const std::vector<mint>& b) { using mint2 = ModInt<MOD>; std::vector<mint2> a2(a.size()), b2(b.size()); for (size_t i = 0; i < a.size(); ++i) { a2[i] = mint2(a[i].x); } for (size_t i = 0; i < b.size(); ++i) { b2[i] = mint2(b[i].x); } return ntt_multiply(sz, a2, b2); } long long combine(int r0, int r1, int r2, int mod) { using mint2 = ModInt<NTT_PRIMES[2]>; static const long long m01 = 1LL * NTT_PRIMES[0] * NTT_PRIMES[1]; static const long long m0_inv_m1 = ModInt<NTT_PRIMES[1]>(NTT_PRIMES[0]).inv().x; static const long long m01_inv_m2 = mint2(m01).inv().x; int v1 = (m0_inv_m1 * (r1 + NTT_PRIMES[1] - r0)) % NTT_PRIMES[1]; auto v2 = (mint2(r2) - r0 - mint2(NTT_PRIMES[0]) * v1) * m01_inv_m2; return (r0 + 1LL * NTT_PRIMES[0] * v1 + m01 % mod * v2.x) % mod; } template<typename mint> std::vector<mint> multiply(const std::vector<mint>& a, const std::vector<mint>& b) { if (a.empty() || b.empty()) return {}; int sz = 1, sz_a = a.size(), sz_b = b.size(); while (sz < sz_a + sz_b) sz <<= 1; if (sz <= 16) { std::vector<mint> res(sz_a + sz_b - 1); for (int i = 0; i < sz_a; ++i) { for (int j = 0; j < sz_b; ++j) { res[i + j] += a[i] * b[j]; } } return res; } int mod = mint::mod(); std::vector<mint> res; if (std::find(std::begin(NTT_PRIMES), std::end(NTT_PRIMES), mod) != std::end(NTT_PRIMES)) { res = ntt_multiply(sz, a, b); } else { auto c0 = convert_mint_and_multiply<NTT_PRIMES[0], mint> (sz, a, b); auto c1 = convert_mint_and_multiply<NTT_PRIMES[1], mint> (sz, a, b); auto c2 = convert_mint_and_multiply<NTT_PRIMES[2], mint> (sz, a, b); res.resize(sz); for (int i = 0; i < sz; ++i) { res[i] = combine(c0[i].x, c1[i].x, c2[i].x, mod); } } res.resize(sz_a + sz_b - 1); return res; } // }}} ``` ## Lucas Theorem ```cpp! //Calculate C(n, k) modulo M with smal M and big n, k const int M=1e5+3; int sub(int a, int b){ return (a<b?a-b+M:a-b); } int add(int a, int b){ return (a+b<M?a+b:a+b-M); } int mul(int a, int b){ return 1LL*a*b%M; } int binpow(int a, int b){ int ans=1; while(b){ if(b&1) ans=mul(ans, a); a=mul(a, a); b>>=1; } return ans; } int inverse(int a){ return binpow(a, M-2); } int fact[M], inv[M]; int C(int NN, int KK){ if(NN<KK) return 0; return mul(fact[NN], mul(inv[KK], inv[NN-KK])); } int Lucas(ll n, ll m){ if(n<m) return 0; int ans=1; for(int i=0; i<4; ++i){ int mod_n=n%M, mod_m=m%M; n/=M; m/=M; ans=mul(ans, C(mod_n, mod_m)); } return ans; } ``` # DP Trick ## Line Container ```cpp! struct Line { mutable ll m, b, p; bool operator<(const Line& o) const { return m < o.m; } bool operator<(ll x) const { return p < x; } }; struct LineContainer : multiset<Line, less<>> { // (for doubles, use inf = 1/.0, div(a,b) = a/b) // Caution : This template solve for finding MAX, so if want to find MIN, NEGATE ALL THE LINE FUNCTION const ll inf = LLONG_MAX; ll div(ll a, ll b) { // floored division return a/b-((a^b)<0&&a%b); } bool isect(iterator x, iterator y) { if (y==end()) { x->p=inf; return false; } if (x->m==y->m) x->p=x->b>y->b?inf:-inf; else x->p=div(y->b-x->b, x->m-y->m); return x->p>=y->p; } void add(ll m, ll b) { auto z=insert({m, b, 0}), y=z++, x=y; while(isect(y, z)) z=erase(z); if (x!=begin() && isect(--x, y)) isect(x, y = erase(y)); while((y = x)!=begin() && (--x)->p>=y->p) isect(x, erase(y)); } ll query(ll x) { // max value at x assert(!empty()); auto l=*lower_bound(x); return l.m*x+l.b; } }; ``` # GRAPH ## Gomory Hu ```cpp! // Usage: // GomoryHu g(n); // g.addEdge(u, v, c) // 1-direction // g.calc() // --> g.answer[i][j] = min cut i-j // // Note: // - When i -> j is not connected, answer[i][j] = INF // - Used together with MaxFlowDinic.h // // Tested: // - https://oj.vnoi.info/problem/mcquery // - https://oj.vnoi.info/problem/icpc21_mn_a /* * Find min cut between every pair of vertices using N max_flow call (instead of N^2) * Not tested with directed graph * Index start from 0 */ struct GomoryHu { int ok[MN], cap[MN][MN]; int answer[MN][MN], parent[MN]; int n; MaxFlow flow; GomoryHu(int _n) : n(_n), flow(_n) { for(int i = 0; i < n; ++i) ok[i] = parent[i] = 0; for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) cap[i][j] = 0, answer[i][j] = INF; } void addEdge(int u, int v, int c) { cap[u][v] += c; } void calc() { for(int i = 1; i <= n-1; ++i) { flow = MaxFlow(n); REP(u,n) REP(v,n) if (cap[u][v]) flow.addEdge(u, v, cap[u][v]); int f = flow.getMaxFlow(i, parent[i]); bfs(i); for(int j = i+1; j < n; ++j) if (ok[j] && parent[j]==parent[i]) parent[j]=i; answer[i][parent[i]] = answer[parent[i]][i] = f; for(int j = 0; j < i; ++j) answer[i][j]=answer[j][i]=min(f,answer[parent[i]][j]); } } void bfs(int start) { memset(ok,0,sizeof ok); queue<int> qu; qu.push(start); while (!qu.empty()) { int u=qu.front(); qu.pop(); for(int xid = 0; xid < (int) flow.g[u].size(); ++xid) { int id = flow.g[u][xid]; int v = flow.e[id].b, fl = flow.e[id].flow, c = flow.e[id].cap; if (!ok[v] && fl < cap) { ok[v]=1; qu.push(v); } } } } }; ``` # DATA STRUCTURE ## Manhattan MST ```cpp! struct Point{ int x, y, id; Point(int x=0, int y=0, int id=0):x(x), y(y), id(id){} }; struct Edge{ int u, v, w; Edge(int u=0, int v=0, int w=0):u(u), v(v), w(w){} bool operator<(const Edge& other) const{ return w<other.w; } }; int n, lab[N], ID[N], EDGE_SZ; Point P[N]; Edge E[N*4]; int root(int u){ return (lab[u]<0?u:(lab[u]=root(lab[u]))); } bool unite(int u, int v){ u=root(u), v=root(v); if(u==v) return false; if(lab[u]>lab[v]) swap(u, v); lab[u]+=lab[v]; lab[v]=u; return true; } void get_edge(){ map<int, int> M; iota(ID+1, ID+1+n, 1); sort(ID+1, ID+1+n, [&](int u, int v){ return (P[u].x+P[u].y)<(P[v].x+P[v].y); }); for(int T=1, i; T<=n; ++T){ i=ID[T]; for(auto iter=M.lower_bound(-P[i].x); iter!=M.end(); M.erase(iter++)){ if((P[iter->second].y-P[i].y)>(P[iter->second].x-P[i].x)) break; E[EDGE_SZ++]=Edge(iter->second, i, P[i].y-P[iter->second].y+P[i].x-P[iter->second].x); } M[-P[i].x]=i; } } void solve(){ cin>>n; for(int i=1; i<=n; ++i){ cin>>P[i].x>>P[i].y; P[i].id=i; } fill(lab+1, lab+1+n, -1); for(int d=0; d<4; ++d){ get_edge(); for(int i=1; i<=n; ++i){ if(d&1) P[i].x*=-1; else swap(P[i].x, P[i].y); } } sort(E, E+EDGE_SZ); ll ans=0; vector<pair<int, int>> pairs; for(int i=0; i<EDGE_SZ; ++i){ if(unite(E[i].u, E[i].v)){ ans+=E[i].w; pairs.push_back({E[i].u-1, E[i].v-1}); } } cout<<ans<<ln; for(auto [u, v]:pairs) cout<<u<<" "<<v<<ln; } ``` ## Wavelet Tree ```cpp! // WaveletMatrix {{{ // Copied from https://github.com/dacin21/dacin21_codebook/blob/master/trees/wavelet_matrix.cpp // // Notes: // - Index from 0 // - k (for k-th query) from 0 // - Need to remove #define int long long // // Tested: // - (kth query) https://judge.yosupo.jp/problem/range_kth_smallest // - (range_count) https://judge.yosupo.jp/problem/static_range_frequency class Bit_Presum{ public: static constexpr uint32_t omega = CHAR_BIT * sizeof(uint64_t); static constexpr uint32_t lg_omega = __lg(omega); static_assert(omega == 64u); Bit_Presum(vector<uint64_t> mask_) : n(mask_.size()), mask(move(mask_)), presum(n+1){ build(); } Bit_Presum(uint32_t bits, bool init_val = 0) : n((bits>>lg_omega) + 1), mask(n, init_val ? ~uint64_t{0} : uint64_t{0}), presum(n+1){ if(init_val) mask.back()<<=((n<<lg_omega) - bits); build(); } /// popcount l <= i < r uint32_t query(uint32_t l, uint32_t r) const { if(__builtin_expect(r < l, false)) return 0; return query(r) - query(l); } /// popcount 0 <= i < x uint32_t query(uint32_t x) const { uint32_t high = x>>lg_omega, low = x & ((uint64_t{1}<<lg_omega) - 1); uint32_t ret = presum_query(high); ret+= __builtin_popcountll(mask[high]& ((uint64_t{1} << low)-1)); return ret; } void update_pre_build(uint32_t x, bool val){ uint32_t high = x>>lg_omega, low = x & ((1u<<lg_omega) - 1); mask[high] = (mask[high] & ~(uint64_t{1} << low)) | (uint64_t{val}<<low); } void do_build(){ build(); } friend ostream& operator<<(ostream&o, Bit_Presum const&b){ for(auto const&e:b.mask){ stringstream ss; ss << bitset<omega>(e); auto s = ss.str(); reverse(s.begin(), s.end()); o << s << "|"; } o << " : "; for(auto const&e:b.presum) o << e << " "; o << "\n"; return o; } private: void presum_build(){ for(uint32_t x=1;x<=n;++x){ presum[x]+=presum[x-1]; } } // sum 0 <= i < x uint32_t presum_query(uint32_t x) const { return presum[x]; } void build(){ for(uint32_t x=0;x<n;++x){ presum[x+1] = __builtin_popcountll(mask[x]); } presum_build(); } const uint32_t n; vector<uint64_t> mask; vector<uint32_t> presum; }; template<typename T, typename Bit_Ds = Bit_Presum> class WaveletMatrix{ public: static_assert(is_integral<T>::value); static constexpr uint32_t height = CHAR_BIT * sizeof(T); WaveletMatrix(vector<T> v): n(v.size()), data(height, n){ build(move(v)); } /// count l <= i < r s.t. A <= val[i] < B uint32_t range_count(int const&l, int const&r, T const&A, T const&B) const { assert(0 <= l && r <= n); return count_lower(l, r, B) - count_lower(l, r, A); } /// count l <= i < r s.t. A <= val[i] uint32_t range_count_up(int const&l, int const&r, T const&A) const { assert(0 <= l && r <= n); if(__builtin_expect(l>r, false)) return uint32_t{0}; return (r-l) - count_lower(l, r, A); } // k from 0 T k_th(int const&l, int const&r, int k) const { assert(0 <= k && k < n); return get_kth(l, r, k); } private: void build(vector<T> v){ m_index.resize(height); T const a = numeric_limits<T>::min(); for(int h = height-1; h>=0;--h){ T const b = a + (T{1}<<(max(0, h-1))) - !h + (T{1}<<(max(0, h-1))); for(int i=0;i<n;++i){ data[h].update_pre_build(i, v[i]<b); } data[h].do_build(); const int m = stable_partition(v.begin(), v.end(), [&b](T const&x){return x < b;}) - v.begin(); for(int i=m;i<n;++i){ v[i] = v[i] - (T{1}<<(max(0, h-1))) + !h - (T{1}<<(max(0, h-1))); } m_index[h] = m; } } /// count l <= i < r s.t. val[i] < B uint32_t count_lower(int l, int r, T const&B) const { assert(0 <= l && r <= n); if(__builtin_expect(r<l, false)) return 0; uint32_t ret = 0; int h = height; T a = numeric_limits<T>::min(); while(h > 0){ --h; bool go_left = B < a + (T{1}<<(max(0, h-1))) - !h + (T{1}<<(max(0, h-1))); const int low_l = data[h].query(l), low_r = data[h].query(r); if(go_left){ l = low_l; r = low_r; } else { a = a + (T{1}<<(max(0, h-1))) - !h + (T{1}<<(max(0, h-1))); ret+= low_r-low_l; l = m_index[h] + l-low_l; r = m_index[h] + r-low_r; } } return ret; } T get_kth(int l, int r, int k) const { assert(0 <= l && r <= n); assert(0 <= k && k < r-l); int h = height; T a = numeric_limits<T>::min(); while(h > 0){ --h; const int low_l = data[h].query(l), low_r = data[h].query(r), low_lr = low_r-low_l; bool go_left = k < low_lr; if(go_left){ l = low_l; r = low_r; } else { a+= T{1}<<h; k-= low_lr; l = m_index[h] + l-low_l; r = m_index[h] + r-low_r; } } return a; } const int n; vector<int> m_index; vector<Bit_Ds> data; }; // }}} ``` ## Partially Persistent DSU ```cpp! struct PersistentDSU{ vector<int> lab, time_united; PersistentDSU(int n):lab(n, -1), time_united(n, INF32){} int root(int u, int ver){ if(time_united[u]>ver) return u; else return root(lab[u], ver); } void unite(int u, int v, int ver){ u=root(u, ver), v=root(v, ver); if(u!=v){ if(lab[u]>lab[v]) swap(u, v); lab[u]+=lab[v]; lab[v]=u; time_united[v]=ver; } } bool same_set(int u, int v, int ver){ return root(u, ver)==root(v, ver); } ``` # STRING ## Update Hash ```cpp! struct hash_BIT { vector<ll> tr, base; ll MOD; hash_BIT (int n) : tr(n + 1, 0) { MOD = 1e9 + 21; base.resize(n + 1); base[0] = 1; for (int i = 1; i <= n; i++) base[i] = base[i-1] * 29LL % MOD; return; } private: int p (int k) { return k & -k; } ll pre (int k) { ll ans = 0; for (int j = k; j > 0; j -= p(j)) ans = (ans + tr[j] * base[k - j]) % MOD; return ans; } public: ll get_hash (int l, int r) { if (l > r) return 0; return (pre(r) - pre(l - 1) * base[r - l + 1] + MOD * MOD) % MOD; } void update (int k, char c) { ll delta = c - 'a' + 1 - get_hash(k, k); for (int j = k; j < tr.size(); j += p(j)) tr[j] = (tr[j] + delta * base[j - k] + MOD * MOD) % MOD; return; } void append (char c) { int pos = tr.size(); tr.push_back((get_hash(pos - p(pos) + 1, pos - 1) * 29LL + c - 'a' + 1) % MOD); return; } }; ``` ## Manacher ```cpp! string t; cin >> t; vector<char> s; s.push_back('.'); for(char d : t){ s.push_back(d); s.push_back('.'); } int n = s.size(); vi best(n); // longest palindrome centered at i int center = 0, lst = 0; for(int i = 1; i < n; ++i){ if(lst > i) best[i] = min(best[center - (i - center)], lst - i); int l = i - best[i], r = i + best[i]; while(l - 1 >= 0 && r + 1 < n && s[l - 1] == s[r + 1]){ --l, ++r; ++best[i]; } if(i + best[i] > lst){ lst = i + best[i]; center = i; } } ll ans = 0; for(int i = 0; i < n; ++i){ best[i] = best[i] * 2 + 1; if(s[i] == '.') ans += (best[i] - 1) / 4; else ans += (best[i] + 1) / 4; } cout << ans; ``` ## Z-function ```cpp! void z_func(string s, int n){ vector<int> z(n); for(int l = 0, r = 0, i = 1; i < n; ++i){ if(i <= r) z[i] = min(z[i - l], r - i + 1); while(i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i]; if(i + z[i] - 1 > r){ l = i; r = i + z[i] - 1; } } } ``` ## Suffix Array ```cpp! // Efficient O(N + alphabet_size) time and space suffix array // For ICPC notebook, it's better to copy a shorter code such as // https://github.com/kth-competitive-programming/kactl/blob/main/content/strings/SuffixArray.h // Usage: // - sa = suffix_array(s, 'a', 'z') // - lcp = LCP(s, sa) // lcp[i] = LCP(sa[i], sa[i+1]) // // Tested: // - SA https://judge.yosupo.jp/problem/suffixarray // - SA https://www.spoj.com/problems/SARRAY/ // - LCP https://judge.yosupo.jp/problem/number_of_substrings // Suffix Array {{{ // Copied from https://judge.yosupo.jp/submission/52300 // Helper functions {{{ void induced_sort(const std::vector<int>& vec, int val_range, std::vector<int>& SA, const std::vector<bool>& sl, const std::vector<int>& lms_idx) { std::vector<int> l(val_range, 0), r(val_range, 0); for (int c : vec) { if (c + 1 < val_range) ++l[c + 1]; ++r[c]; } std::partial_sum(l.begin(), l.end(), l.begin()); std::partial_sum(r.begin(), r.end(), r.begin()); std::fill(SA.begin(), SA.end(), -1); for (int i = (int)lms_idx.size() - 1; i >= 0; --i) SA[--r[vec[lms_idx[i]]]] = lms_idx[i]; for (int i : SA) if (i >= 1 && sl[i - 1]) SA[l[vec[i - 1]]++] = i - 1; std::fill(r.begin(), r.end(), 0); for (int c : vec) ++r[c]; std::partial_sum(r.begin(), r.end(), r.begin()); for (int k = (int)SA.size() - 1, i = SA[k]; k >= 1; --k, i = SA[k]) if (i >= 1 && !sl[i - 1]) { SA[--r[vec[i - 1]]] = i - 1; } } std::vector<int> SA_IS(const std::vector<int>& vec, int val_range) { const int n = vec.size(); std::vector<int> SA(n), lms_idx; std::vector<bool> sl(n); sl[n - 1] = false; for (int i = n - 2; i >= 0; --i) { sl[i] = (vec[i] > vec[i + 1] || (vec[i] == vec[i + 1] && sl[i + 1])); if (sl[i] && !sl[i + 1]) lms_idx.push_back(i + 1); } std::reverse(lms_idx.begin(), lms_idx.end()); induced_sort(vec, val_range, SA, sl, lms_idx); std::vector<int> new_lms_idx(lms_idx.size()), lms_vec(lms_idx.size()); for (int i = 0, k = 0; i < n; ++i) if (!sl[SA[i]] && SA[i] >= 1 && sl[SA[i] - 1]) { new_lms_idx[k++] = SA[i]; } int cur = 0; SA[n - 1] = cur; for (size_t k = 1; k < new_lms_idx.size(); ++k) { int i = new_lms_idx[k - 1], j = new_lms_idx[k]; if (vec[i] != vec[j]) { SA[j] = ++cur; continue; } bool flag = false; for (int a = i + 1, b = j + 1;; ++a, ++b) { if (vec[a] != vec[b]) { flag = true; break; } if ((!sl[a] && sl[a - 1]) || (!sl[b] && sl[b - 1])) { flag = !((!sl[a] && sl[a - 1]) && (!sl[b] && sl[b - 1])); break; } } SA[j] = (flag ? ++cur : cur); } for (size_t i = 0; i < lms_idx.size(); ++i) lms_vec[i] = SA[lms_idx[i]]; if (cur + 1 < (int)lms_idx.size()) { auto lms_SA = SA_IS(lms_vec, cur + 1); for (size_t i = 0; i < lms_idx.size(); ++i) { new_lms_idx[i] = lms_idx[lms_SA[i]]; } } induced_sort(vec, val_range, SA, sl, new_lms_idx); return SA; } // }}} template<typename ContainerT = std::string, typename ElemT = unsigned char> std::vector<int> suffix_array(const ContainerT& s, const ElemT first = 'a', const ElemT last = 'z') { std::vector<int> vec(s.size() + 1); std::copy(std::begin(s), std::end(s), std::begin(vec)); for (auto& x : vec) x -= (int)first - 1; vec.back() = 0; auto ret = SA_IS(vec, (int)last - (int)first + 2); ret.erase(ret.begin()); return ret; } std::vector<int> LCP(const std::string& s, const std::vector<int>& sa) { int n = s.size(), k = 0; std::vector<int> lcp(n), rank(n); for (int i = 0; i < n; i++) rank[sa[i]] = i; for (int i = 0; i < n; i++, k ? k-- : 0) { if (rank[i] == n - 1) { k = 0; continue; } int j = sa[rank[i] + 1]; while (i + k < n && j + k < n && s[i + k] == s[j + k]) k++; lcp[rank[i]] = k; } lcp[n - 1] = 0; return lcp; } int64_t cnt_distinct_substrings(const std::string& s) { auto lcp = LCP(s, suffix_array(s, 0, 255)); return s.size() * (int64_t) (s.size() + 1) / 2 - std::accumulate(lcp.begin(), lcp.end(), 0LL); } ``` ## Prefix Function ```cpp! vector<int> prefixFunc (string s) { int n = s.length(); vector<int> pi(n); for (int i = 1; i < n; i++) { int j = pi[i - 1]; while (j && s[j] != s[i]) j = pi[j - 1]; if (s[j] == s[i]) pi[i] = j + 1; } return pi; } ``` # GEO ## Basic ```cpp! template<class T> struct Point{ T x, y; Point(T _x=0, T _y=0):x(_x), y(_y){} template<class U> operator Point<U>(){ return Point<U>(U(x), U(y)); } Point &operator+=(Point p){ x+=p.x; y+=p.y; return *this; } Point &operator-=(Point p){ x-=p.x; y-=p.y; return *this; } Point &operator*=(T v){ x*=v; y*=v; return *this; } Point &operator/=(T v){ x/=v; y/=v; return *this; } Point operator-() const{ return Point(-x, -y); } friend Point operator+(Point a, Point b){ return a+=b; } friend Point operator-(Point a, Point b){ return a-=b; } friend Point operator*(Point a, T v){ return a*=v; } friend Point operator*(T v, Point a){ return a*=v; } friend Point operator/(Point a, T v){ return a/=v; } friend bool operator==(Point a, Point b){ return a.x==b.x and a.y==b.y; } friend istream& operator>>(istream& in, Point& p){ return in>>p.x>>p.y; } friend ostream& operator<<(ostream& out, Point p){ return out<<p.x<<' '<<p.y; } }; template<class T> T dot(Point<T> a, Point<T> b){ return a.x*b.x+a.y*b.y; } template<class T> T cross(Point<T> a, Point<T> b){ return a.x*b.y-a.y*b.x; } template<class T> T dot(Point<T> a, Point<T> b, Point<T> c){ return dot(b-a, c-a); } template<class T> T cross(Point<T> a, Point<T> b, Point<T> c){ return cross(b-a, c-a); } template<class T> T square(Point<T> a){ return dot(a, a); } template<class T> double length(Point<T> p){ return sqrt(square(p)); } long double length(Point<long double> p){ return sqrt(square(p)); } template<class T> Point<T> normalize(Point<T> p){ return p/length(p); } template<class T> T manhattan(Point<T> a, Point<T> b){ return abs(a.x-b.x)+abs(a.y-b.y); } template<class T> int sign(T x){ return (x==0?0:(x<0?-1:1)); } //segment AB, point C template<class T> bool onSegment(Point<T> a, Point<T> b, Point<T> c){ return cross(a-c, b-c)==0 and dot(a-c, b-c)<=0; } //segment AB, CD template<class T> bool intersect(Point<T> a, Point<T> b, Point<T> c, Point<T> d){ return onSegment(a, b, c) or onSegment(a, b, d) or onSegment(c, d, a) or onSegment(c, d, b) or (sign(cross(a, b, c))*sign(cross(a, b, d))<0 and sign(cross(c, d, a))*sign(cross(c, d, b))<0); } using point=Point<long long>; template<class T> struct Line{ T a, b, c; //ax+by+c=0 Line(Point<T> A, Point<T> B){ a=B.y-A.y; b=A.x-B.x; c=-A.x*B.y+A.y*B.x; } Line(T _a=0, T _b=0, T _c=0){ a=_a, b=_b, c=_c; } T func(T x, T y){ return a*x+b*y+c; } friend istream& operator>>(istream& in, Line& d){ return in>>d.a>>d.b>>d.c; } }; //line AB, point C template<class T> long double LineToPoint(Point<T> a, Point<T> b, Point<T> c){ return abs(cross(a, b, c))/length(a-b); } //line AB, point C template<class T> long double LineToPoint(Line<T> d, Point<T> p){ return abs(d.func(p.x, p.y))/sqrt(d.a*d.a+d.b*d.b); } using line=Line<long long>; //line AB, CD template<class T> bool parallel(Point<T> a, Point<T> b, Point<T> c, Point<T> d){ Line<T> d1(a, b), d2(c, d); return (d1.a*d2.b==d1.b*d2.a); } //ray AB, point C template<class T> long double RayToPoint(Point<T> a, Point<T> b, Point<T> c){ return (dot(a, b, c)>=0?LineToPoint(a, b, c):min(length(a-c), length(b-c))); } ``` ## Polygon ```cpp! // Polygon: convex hull, in polygon, convex diameter .. // Functions with param vector<P<T>> works with both integers and floating points // Functions with param Polygon works with floating points only. typedef vector< Point > Polygon; // Convex Hull: // If minimum point --> #define REMOVE_REDUNDANT // Known issues: // - Max. point does not work when some points are the same. // Tested: // - (min points) https://open.kattis.com/problems/convexhull // - (max points) https://cses.fi/problemset/task/2195 // #define REMOVE_REDUNDANT template<typename T> T area2(P<T> a, P<T> b, P<T> c) { return a%b + b%c + c%a; } template<typename T> void ConvexHull(vector<P<T>> &pts) { sort(pts.begin(), pts.end()); pts.erase(unique(pts.begin(), pts.end()), pts.end()); vector<P<T>> up, dn; for (int i = 0; i < (int) pts.size(); i++) { #ifdef REMOVE_REDUNDANT while (up.size() > 1 && area2(up[up.size()-2], up.back(), pts[i]) >= 0) up.pop_back(); while (dn.size() > 1 && area2(dn[dn.size()-2], dn.back(), pts[i]) <= 0) dn.pop_back(); #else while (up.size() > 1 && area2(up[up.size()-2], up.back(), pts[i]) > 0) up.pop_back(); while (dn.size() > 1 && area2(dn[dn.size()-2], dn.back(), pts[i]) < 0) dn.pop_back(); #endif up.push_back(pts[i]); dn.push_back(pts[i]); } pts = dn; for (int i = (int) up.size() - 2; i >= 1; i--) pts.push_back(up[i]); #ifdef REMOVE_REDUNDANT if (pts.size() <= 2) return; dn.clear(); dn.push_back(pts[0]); dn.push_back(pts[1]); for (int i = 2; i < (int) pts.size(); i++) { if (onSegment(dn[dn.size()-2], pts[i], dn.back())) dn.pop_back(); dn.push_back(pts[i]); } if (dn.size() >= 3 && onSegment(dn.back(), dn[1], dn[0])) { dn[0] = dn.back(); dn.pop_back(); } pts = dn; #endif } // Area, perimeter, centroid template<typename T> T signed_area2(vector<P<T>> p) { T area(0); for(int i = 0; i < (int) p.size(); i++) { area += p[i] % p[(i + 1) % p.size()]; } return area; } double area(const Polygon &p) { return std::abs(signed_area2(p) / 2.0); } Point centroid(Polygon p) { Point c(0,0); double scale = 3.0 * signed_area2(p); for (int i = 0; i < (int) p.size(); i++){ int j = (i+1) % p.size(); c = c + (p[i]+p[j])*(p[i].x*p[j].y - p[j].x*p[i].y); } return c / scale; } double perimeter(Polygon p) { double res = 0; for(int i = 0; i < (int) p.size(); ++i) { int j = (i + 1) % p.size(); res += (p[i] - p[j]).len(); } return res; } // Is convex: checks if polygon is convex. // Return true for degen points (all vertices are collinear) template<typename T> bool is_convex(const vector<P<T>> &P) { int sz = (int) P.size(); int min_ccw = 2, max_ccw = -2; for (int i = 0; i < sz; i++) { int c = ccw(P[i], P[(i+1) % sz], P[(i+2) % sz]); min_ccw = min(min_ccw, c); max_ccw = max(max_ccw, c); } return min_ccw * max_ccw >= 0; } // Inside polygon: O(N). Works with any polygon // Tested: // - https://open.kattis.com/problems/pointinpolygon // - https://open.kattis.com/problems/cuttingpolygon enum PolygonLocation { OUT, ON, IN }; PolygonLocation in_polygon(const Polygon &p, Point q) { if ((int)p.size() == 0) return PolygonLocation::OUT; // Check if point is on edge. int n = p.size(); REP(i,n) { int j = (i + 1) % n; Point u = p[i], v = p[j]; if (u > v) swap(u, v); if (ccw(u, v, q) == 0 && u <= q && q <= v) return PolygonLocation::ON; } // Check if point is strictly inside. int c = 0; for (int i = 0; i < n; i++) { int j = (i + 1) % n; if (((p[i].y <= q.y && q.y < p[j].y) || (p[j].y <= q.y && q.y < p[i].y)) && q.x < p[i].x + (p[j].x - p[i].x) * (q.y - p[i].y) / (double) (p[j].y - p[i].y)) { c = !c; } } return c ? PolygonLocation::IN : PolygonLocation::OUT; } // Check point in convex polygon, O(logN) #define Det(a,b,c) ((double)(b.x-a.x)*(double)(c.y-a.y)-(double)(b.y-a.y)*(c.x-a.x)) PolygonLocation in_convex(vector<Point>& l, Point p){ if (l.empty()) return PolygonLocation::OUT; if (l.size() <= 2) { return onSegment(l[0], l[1 % l.size()], p) ? PolygonLocation::ON : PolygonLocation::OUT; } int a = 1, b = l.size()-1, c; if (Det(l[0], l[a], l[b]) > 0) swap(a,b); if (onSegment(l[0], l[a], p)) return ON; if (onSegment(l[0], l[b], p)) return ON; if (Det(l[0], l[a], p) > 0 || Det(l[0], l[b], p) < 0) return OUT; while(abs(a-b) > 1) { c = (a+b)/2; if (Det(l[0], l[c], p) > 0) b = c; else a = c; } int t = cmp(Det(l[a], l[b], p), 0); return (t == 0) ? ON : (t < 0) ? IN : OUT; } // Cut a polygon with a line. Returns half on left-hand-side. // To return the other half, reverse the direction of Line l (by negating l.a, l.b) // The line must be formed using 2 points Polygon polygon_cut(const Polygon& P, Line l) { Polygon Q; for(int i = 0; i < (int) P.size(); ++i) { Point A = P[i], B = (i == ((int) P.size())-1) ? P[0] : P[i+1]; if (ccw(l.A, l.B, A) != -1) Q.push_back(A); if (ccw(l.A, l.B, A)*ccw(l.A, l.B, B) < 0) { Point p; areIntersect(Line(A, B), l, p); Q.push_back(p); } } return Q; } // Find intersection of 2 convex polygons // Helper method bool intersect_1pt(Point a, Point b, Point c, Point d, Point &r) { double D = (b - a) % (d - c); if (cmp(D, 0) == 0) return false; double t = ((c - a) % (d - c)) / D; double s = -((a - c) % (b - a)) / D; r = a + (b - a) * t; return cmp(t, 0) >= 0 && cmp(t, 1) <= 0 && cmp(s, 0) >= 0 && cmp(s, 1) <= 0; } Polygon convex_intersect(Polygon P, Polygon Q) { const int n = P.size(), m = Q.size(); int a = 0, b = 0, aa = 0, ba = 0; enum { Pin, Qin, Unknown } in = Unknown; Polygon R; do { int a1 = (a+n-1) % n, b1 = (b+m-1) % m; double C = (P[a] - P[a1]) % (Q[b] - Q[b1]); double A = (P[a1] - Q[b]) % (P[a] - Q[b]); double B = (Q[b1] - P[a]) % (Q[b] - P[a]); Point r; if (intersect_1pt(P[a1], P[a], Q[b1], Q[b], r)) { if (in == Unknown) aa = ba = 0; R.push_back( r ); in = B > 0 ? Pin : A > 0 ? Qin : in; } if (C == 0 && B == 0 && A == 0) { if (in == Pin) { b = (b + 1) % m; ++ba; } else { a = (a + 1) % m; ++aa; } } else if (C >= 0) { if (A > 0) { if (in == Pin) R.push_back(P[a]); a = (a+1)%n; ++aa; } else { if (in == Qin) R.push_back(Q[b]); b = (b+1)%m; ++ba; } } else { if (B > 0) { if (in == Qin) R.push_back(Q[b]); b = (b+1)%m; ++ba; } else { if (in == Pin) R.push_back(P[a]); a = (a+1)%n; ++aa; } } } while ( (aa < n || ba < m) && aa < 2*n && ba < 2*m ); if (in == Unknown) { if (in_convex(Q, P[0])) return P; if (in_convex(P, Q[0])) return Q; } return R; } // Find the diameter of polygon. // Returns: // <diameter, <ids of 2 points>> pair<double, pair<int,int>> convex_diameter(const Polygon &p) { const int n = p.size(); int is = 0, js = 0; for (int i = 1; i < n; ++i) { if (p[i].y > p[is].y) is = i; if (p[i].y < p[js].y) js = i; } double maxd = (p[is]-p[js]).len(); int i, maxi, j, maxj; i = maxi = is; j = maxj = js; do { int ii = (i+1) % n, jj = (j+1) % n; if ((p[ii] - p[i]) % (p[jj] - p[j]) >= 0) j = jj; else i = ii; if ((p[i] - p[j]).len() > maxd) { maxd = (p[i] - p[j]).len(); maxi = i; maxj = j; } } while (i != is || j != js); return {maxd, std::minmax(maxi, maxj)}; /* farthest pair is (maxi, maxj). */ } // Pick theorem // Given non-intersecting polygon. // S = area // I = number of integer points strictly Inside // B = number of points on sides of polygon // S = I + B/2 - 1 ```