note2
note2
PTIT.Nutriboost
December 11, 2024
1
PTIT.Nutriboost 2
1 Algorithms Case 2: P != u // we need to handle the case where we update an index that
Our query would be in range [EN(u), ST(v)] + [ST(p), ST(p)] is inside
1.1 Mo’s Algorithm */ // [cur_l, cur_r]
//
void update(int &L, int &R, int qL, int qR){ // Mo algorithm with updates {{{
/* while (L > qL) add(--L); enum QueryType { GET = 0, UPDATE = 1 };
https://www.spoj.com/problems/FREQ2/ while (R < qR) add(++R);
*/ struct Query {
vector <int> MoQueries(int n, vector <query> Q){ while (L < qL) del(L++); int l, r; // For get
while (R > qR) del(R--); int u, val, old_val; // For update
block_size = sqrt(n); } int id;
sort(Q.begin(), Q.end(), [](const query &A, const query &B){ QueryType typ;
return (A.l/block_size != B.l/block_size)? vector <int> MoQueries(int n, vector <query> Q){ };
(A.l/block_size < B.l/block_size) : (A.r < B.r); block_size = sqrt((int)nodes.size());
}); sort(Q.begin(), Q.end(), [](const query &A, const query &B){ template<typename Add, typename Rem, typename Update, typename
vector <int> res; return (ST[A.l]/block_size != ST[B.l]/block_size)? Get>
res.resize((int)Q.size()); (ST[A.l]/block_size < ST[B.l]/block_size) : void mo_with_updates(
(ST[A.r] < ST[B.r]); int n, const vector<Query>& queries,
int L = 1, R = 0; }); Add add, Rem rem, Update update, Get get) {
for(query q: Q){ vector <int> res; // Separate update and get queries
while (L > q.l) add(--L); res.resize((int)Q.size()); vector<Query> updates, gets;
while (R < q.r) add(++R); for (const auto& query : queries) {
LCA lca; if (query.typ == QueryType::UPDATE)
while (L < q.l) del(L++); lca.initialize(n); updates.push_back(query);
while (R > q.r) del(R--); else gets.push_back(query);
int L = 1, R = 0; }
res[q.pos] = calc(1, R-L+1); for(query q: Q){
} int u = q.l, v = q.r; // Sort queries
return res; if(ST[u] > ST[v]) swap(u, v); // assume that S[u] <= int S = std::max<int>(1, cbrtl(n + 0.5));
} S[v] S = S * S;
int parent = lca.get(u, v);
sort(gets.begin(), gets.end(), [&] (const Query& q1, const
if(parent == u){ Query& q2) {
1.2 Mo’s Algorithms on Trees int qL = ST[u], qR = ST[v]; int l1 = q1.l / S;
update(L, R, qL, qR); int l2 = q2.l / S;
}else{ if (l1 != l2) return l1 < l2;
/* int qL = EN[u], qR = ST[v];
Given a tree with N nodes and Q queries. Each node has an update(L, R, qL, qR); int r1 = q1.r / S;
integer weight. if(cnt_val[a[parent]] == 0) int r2 = q2.r / S;
Each query provides two numbers u and v, ask for how many res[q.pos] += 1; if (r1 != r2) return (l1 % 2 == 0) ? r1 < r2 : r1 >
different integers weight of nodes } r2;
there are on path from u to v.
res[q.pos] += cur_ans; return (r1 % 2 == 0)
---------- } ? q1.id < q2.id
Modify DFS: return res; : q1.id > q2.id;
---------- } });
For each node u, maintain the start and the end DFS time. Let’s
call them ST(u) and EN(u). // Process queries
=> For each query, a node is considered if its occurrence count int cur_l = -1, cur_r = -1, cur_update = -1;
is one. 1.3 Mo’s With Update for (const auto& query : gets) {
// move to [l, r]
-------------- if (cur_l < 0) {
Query solving: // Tested: for (int i = query.l; i <= query.r; ++i) add(i);
-------------- // - https://www.spoj.com/problems/ADAUNIQ/ cur_l = query.l;
Let’s query be (u, v). Assume that ST(u) <= ST(v). Denotes P as // cur_r = query.r;
LCA(u, v). //Notes: } else {
//- Updates must be set: A(u) = val while (cur_l > query.l) add(--cur_l);
Case 1: P = u //- When implementing Update(id, new_value, cur_l, cur_r) -> while (cur_r < query.r) add(++cur_r);
Our query would be in range [ST(u), ST(v)]. void: while (cur_r > query.r) rem(cur_r--);
// [cur_l, cur_r] = current segment while (cur_l < query.l) rem(cur_l++);
PTIT.Nutriboost 3
} return 0; //
} // 0-based
// process updates // DSU with rollback {{{
// should we update more? void work() struct Data {
while (cur_update + 1 < (int) updates.size() { int time, u, par; // before ‘time‘, ‘par‘ = par[u]
&& updates[cur_update + 1].id < query.id) { for(int i=1;i<=q;i++) };
++cur_update; vec[i].clear();
update(updates[cur_update].u, for(int i=1;i<=n;i++) struct DSU {
updates[cur_update].val, cur_l, cur_r); if(mid[i]>0) vector<int> par;
} vec[mid[i]].push_back(i); vector<Data> change;
// should we update less? clear();
while (cur_update >= 0 && updates[cur_update].id > for(int i=1;i<=q;i++) DSU(int n) : par(n + 5, -1) {}
query.id) { {
update(updates[cur_update].u, apply(i); // find root of x.
updates[cur_update].old_val, cur_l, cur_r); for(auto &it:vec[i]) //Add appropriate check // if par[x] < 0 then x is a root, and its tree has -par[x]
--cur_update; conditions nodes
} { int getRoot(int x) {
if(check(it)) while (par[x] >= 0)
get(query); hi[it]=i; x = par[x];
} else return x;
} lo[it]=i+1; }
// }}} }
} bool same_component(int u, int v) {
} return getRoot(u) == getRoot(v);
}
1.4 Parallel Binary Search void parallel_binary()
{ // join components containing x and y.
for(int i=1;i<=n;i++) // t should be current time. We use it to update ‘change‘.
int lo[N], mid[N], hi[N]; lo[i]=1, hi[i]=q+1; bool join(int x, int y, int t) {
vector<int> vec[N]; bool changed = 1; x = getRoot(x);
while(changed) y = getRoot(y);
void clear() //Reset { if (x == y) return false;
{ changed=0;
memset(bit, 0, sizeof(bit)); for(int i=1;i<=n;i++) //union by rank
} { if (par[x] < par[y]) swap(x, y);
if(lo[i]<hi[i]) //now x’s tree has less nodes than y’s tree
void apply(int idx) //Apply ith update/query { change.push_back({t, y, par[y]});
{ changed=1; par[y] += par[x];
if(ql[idx] <= qr[idx]) mid[i]=(lo[i] + hi[i])/2; change.push_back({t, x, par[x]});
update(ql[idx], qa[idx]), update(qr[idx]+1, } par[x] = y;
-qa[idx]); else return true;
else mid[i]=-1; }
{ }
update(1, qa[idx]); work(); // rollback all changes at time > t.
update(qr[idx]+1, -qa[idx]); } void rollback(int t) {
update(ql[idx], qa[idx]); } while (!change.empty() && change.back().time > t) {
} par[change.back().u] = change.back().par;
} change.pop_back();
}
bool check(int idx) //Check if the condition is satisfied }
{
2 Data Structures };
int req=reqd[idx]; // }}}
for(auto &it:owns[idx]) 2.1 DSU Roll Back
{
req-=pref(it);
if(req<0) // Tested: 2.2 HLD with Euler Tour
break; // - (dynamic connectivity)
} https://codeforces.com/gym/100551/problem/A
if(req<=0) // - (used for directed MST) /*
return 1; https://judge.yosupo.jp/problem/directedmst HLD + Euler Tour combine:
PTIT.Nutriboost 4
if (ps[i].x - ps[i].y > ps[j].x - ps[j].y) break; 5.12 Push Relabel else ++cur[u];
assert(ps[i].x >= ps[j].x && ps[i].y >= ps[j].y); }
edges.push_back({(ps[i].x - ps[j].x) + (ps[i].y }
- ps[j].y), i, j}); struct PushRelabel { bool leftOfMinCut(int a) { return H[a] >= sz(g); }
} struct Edge { };
active[ps[i].x] = i; int dest, back;
} ll f, c;
for (auto &p : ps) { // rotate };
if (rot & 1) p.x *= -1; vector<vector<Edge>> g; 5.13 Tarjan SCC
else swap(p.x, p.y); vector<ll> ec;
} vector<Edge*> cur;
} vector<vi> hs; vi H; const int N = 20002;
return edges; PushRelabel(int n) : g(n), ec(n), cur(n), hs(2*n), H(n) struct tarjan_scc {
} {} int scc[MN], low[MN], d[MN], stacked[MN];
int ticks, current_scc;
void addEdge(int s, int t, ll cap, ll rcap=0) { deque<int> s; // used as stack
if (s == t) return; tarjan_scc() {}
5.10 Minimum Path Cover in DAG g[s].push_back({t, sz(g[t]), 0, cap}); void init() {
g[t].push_back({s, sz(g[s])-1, 0, rcap}); memset(scc, -1, sizeof(scc));
Given a directed acyclic graph G = (V, E), we are to find the } memset(d, -1, sizeof(d));
minimum number of vertex-disjoint paths to cover each vertex in memset(stacked, 0, sizeof(stacked));
V. void addFlow(Edge& e, ll f) { s.clear();
We can construct a bipartite graph G′ = (V out ∪ V in, E ′ ) Edge &back = g[e.dest][e.back]; ticks = current_scc = 0;
if (!ec[e.dest] && f) }
from G, where :
hs[H[e.dest]].push_back(e.dest); void compute(vector<vector<int>> &g, int u) {
e.f += f; e.c -= f; ec[e.dest] += f; d[u] = low[u] = ticks++;
back.f -= f; back.c += f; ec[back.dest] -= f; s.push_back(u);
V out = {v ∈ V : v has positive out − degree} } stacked[u] = true;
ll calc(int s, int t) { for (int i = 0; i < g[u].size(); i++) {
V in = {v ∈ V : v has positive in − degree} int v = g[u][i];
int v = sz(g); H[s] = v; ec[t] = 1;
E ′ = {(u, v) ∈ V out × V in : (u, v) ∈ E} vi co(2*v); co[0] = v-1; if (d[v] == -1) compute(g, v);
rep(i,0,v) cur[i] = g[i].data(); if (stacked[v]) low[u] = min(low[u], low[v]);
Then it can be shown, via König’s theorem, that G’ has a for (Edge& e : g[s]) addFlow(e, e.c); }
matching of size m if and only if there exists n−m vertex-disjoint if (d[u] == low[u]) {
paths that cover each vertex in G, where n is the number of ver- for (int hi = 0;;) { int v;
tices in G and m is the maximum cardinality bipartite mathching while (hs[hi].empty()) if (!hi--) return do {
-ec[s]; v = s.back(); s.pop_back();
in G’.
int u = hs[hi].back(); hs[hi].pop_back(); stacked[v] = false;
while (ec[u] > 0) // discharge u scc[v] = current_scc;
Therefore, the problem can be solved by finding the maximum if (cur[u] == g[u].data() + } while (u != v);
cardinality matching in G’ instead. sz(g[u])) { current_scc++;
NOTE: If the paths are note necesarily disjoints, find the H[u] = 1e9; }
transitive closure and solve the problem for disjoint paths. for (Edge& e : g[u]) if }
(e.c && H[u] > };
H[e.dest]+1)
5.11 Planar Graph (Euler) H[u] = H[e.dest]+1,
cur[u] = &e;
Euler’s formula states that if a finite, connected, planar graph is if (++co[H[u]], !--co[hi] 5.14 Topological Sort
drawn in the plane without any edge intersections, and v is the && hi < v)
number of vertices, e is the number of edges and f is the number rep(i,0,v) if (hi <
of faces (regions bounded by edges, including the outer, infinitely H[i] && H[i] < vi topoSort(const vector<vi>& gr) {
v) vi indeg(sz(gr)), ret;
large region), then: --co[H[i]], for (auto& li : gr) for (int x : li) indeg[x]++;
H[i] = queue<int> q; // use priority_queue for lexic. largest
f +v =e+2 v + 1; ans.
hi = H[u]; rep(i,0,sz(gr)) if (indeg[i] == 0) q.push(i);
It can be extended to non connected planar graphs with c
} else if (cur[u]->c && H[u] == while (!q.empty()) {
connected components: H[cur[u]->dest]+1) int i = q.front(); // top() for priority queue
addFlow(*cur[u], min(ec[u], ret.push_back(i);
f +v =e+c+1 cur[u]->c)); q.pop();
PTIT.Nutriboost 16
for (int x : gr[i]) vector <int> adj_vt[N]; for (int i = 1; i < n; ++i) {
if (--indeg[x] == 0) q.push(x); int vt_root(vector <int> &ver) { int u, v;
} sort(ver.begin(), ver.end(), [&] (const int& x, const int& cin >> u >> v;
return ret; y) { adj[u].push_back(v);
} return st[x] < st[y]; adj[v].push_back(u);
}); }
int m = ver.size();
for (int i = 0; i + 1 < m; ++i) { dfs(1, 0);
5.15 Virtual Tree int new_ver = lca(ver[i], ver[i + 1]);
ver.push_back(new_ver); for (int _q = 1; _q <= q; ++_q) {
} int k;
/* sort(ver.begin(), ver.end(), [&] (const int& x, const int& cin >> k;
Used to solve problem with set of vertices y) {
return st[x] < st[y];
https://www.hackerrank.com/contests/hourrank-15/challenges/kittys-calculations-on-a-tree vector <int> ver;
*/ }); tot = 0;
ver.resize(unique(ver.begin(), ver.end()) - ver.begin()); while (k--) {
const int MOD = 1e9 + 7; int x; cin >> x;
const int N = 2e5 + 5; stack <int> stk; sz[x] = x;
const int K = 18; stk.push(ver[0]); tot = (tot + x) % MOD;
m = ver.size(); ver.push_back(x);
vector <int> adj[N]; for (int i = 1; i < m; ++i) { }
int st[N], en[N], dep[N]; int u = ver[i];
int up[K][N]; while (!stk.empty() && !inside(stk.top(), u)) // check int rt = vt_root(ver);
int timer = 0; if v is in u’s subtree solve(rt, 0);
stk.pop(); cout << ans << "\n";
// LCA adj_vt[stk.top()].push_back(u);
void dfs(int u, int p) { stk.push(u); for (int x : ver) {
st[u] = ++timer; } sz[x] = 0;
for (int v : adj[u]) { return ver[0]; adj_vt[x].clear();
if (v == p) continue; } }
dep[v] = dep[u] + 1; ans = 0;
up[0][v] = u; int sz[N]; }
for (int i = 1; i < K; ++i) int tot; // total special vertices return 0;
up[i][v] = up[i - 1][up[i - 1][v]]; ll ans; }
dfs(v, u); void solve(int u, int p) {
} for (int v : adj_vt[u]) {
en[u] = timer; if (v == p) continue;
solve(v, u);
}
sz[u] = (sz[u] + sz[v]) % MOD; 6 Linear Algebra
int lca(int u, int v) {
if (dep[u] != dep[v]) { }
if (dep[u] < dep[v]) swap(u, v); 6.1 Matrix Determinant
int d = dep[u] - dep[v]; for (int v : adj_vt[u]) {
for (int i = K - 1; i >= 0; --i) if (v == p) continue;
if (d & (1 << i)) int w = dep[v] - dep[u]; double det(vector<vector<double>>& a) {
u = up[i][u]; int mul = 1LL * sz[v] * (tot - sz[v] + MOD) % MOD; int n = sz(a); double res = 1;
} ans += 1LL * w * mul % MOD; rep(i,0,n) {
if (u == v) return u; ans %= MOD; int b = i;
for (int i = K - 1; i >= 0; --i) { } rep(j,i+1,n) if (fabs(a[j][i]) > fabs(a[b][i]))
if (up[i][u] != up[i][v]) { } b = j;
u = up[i][u]; if (i != b) swap(a[i], a[b]), res *= -1;
v = up[i][v]; signed main() { res *= a[i][i];
} cin.tie(0) -> sync_with_stdio(0); if (res == 0) return 0;
} rep(j,i+1,n) {
return up[0][u]; #ifdef JASPER double v = a[j][i] / a[i][i];
} freopen("in1", "r", stdin); if (v != 0) rep(k,i+1,n) a[j][k] -= v *
#endif a[i][k];
bool inside(int u, int v) { }
return st[u] <= st[v] && en[v] <= en[u]; int n, q; }
} cin >> n >> q; return res;
/// }
PTIT.Nutriboost 17
7.4 General purpose numbers # on k existing trees of size ni : n1 n2 · · · nk nk−2 7.7 Mobius
# with degrees di : (n − 2)!/((d1 − 1)! · · · (dn − 1)!)
Bernoulli numbers
t Catalan numbers
EGF of Bernoulli numbers is B(t) = et −1
(FFT-able).
B[0, . . .] = [1, − 21 , 16 , 0, − 30
1 1
, 0, 42 , . . .] 1 2n 2n 2n (2n)! /*
Cn = = − =
Sums of powers: n+1 n n n+1 (n + 1)!n! Nu c c l s chnh phng khc 1 -> 0
n m
Nu c l c nguyn t -> -1
1 m + 1
Nu c chn c nguyn t -> 1
2(2n + 1)
X m
X m+1−k
n = Bk · (n + 1)
X
m+1 k C0 = 1, Cn+1 = Cn , Cn+1 = Ci Cn−i mob(1) = 1;
i=1 k=0 n+2 */
Euler-Maclaurin formula for infinite sums: Cn = 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, . . .
∞ ∞ const int N = 5e5 + 9;
Z ∞ Bk (k−1) [noitemsep]sub-diagonal monotone paths in an n × n grid.
X X
f (i) = f (x)dx − f (m) int mob[N];
i=m m k=1
k! strings with n pairs of parenthesis, correctly nested. binary void mobius() {
trees with with n + 1 leaves (0 or 2 children). ordered trees mob[1] = 1;
∞ f (m) f ′ (m) f ′′′ (m)
Z
(5) for (int i = 2; i < N; i++){
≈ f (x)dx + − + + O(f (m)) with n + 1 vertices. ways a convex polygon with n + 2 mob[i]--;
m 2 12 720
sides can be cut into triangles by connecting vertices with for (int j = i + i; j < N; j += i) {
Stirling numbers of the first kind straight lines. permutations of [n] with no 3-term increas- mob[j] -= mob[i];
Number of permutations on n items with k cycles. ing subseq. }
}
c(n, k) = c(n − 1, k − 1) + (n − 1)c(n − 1, k), c(0, 0) = 1 }
Pn k 7.5 Lucas Theorem
k=0 c(n, k)x = x(x + 1) . . . (x + n − 1)
For non-negative integers m and n and a prime p, the following
c(8, k) = 8, 0, 5040, 13068, 13132, 6769, 1960, 322, 28, 1 congruence relation holds: :
Stirling numbers of the second kind
Partitions of n distinct elements into exactly k groups. m k
Y mi
≡ (mod p),
S(n, k) = S(n − 1, k − 1) + kS(n − 1, k) n i=0
ni
7.9 Number Theoretic Transform vector<int> multiply(vector<int> &a, vector<int> &b, int eq = a = multiply(a, a, 1);
0) { p >>= 1;
int need = a.size() + b.size() - 1; }
#include<bits/stdc++.h> int p = 0; return res;
using namespace std; while((1 << p) < need) p++; }
ensure_base(p); int main() {
const int N = 3e5 + 9, mod = 998244353; int sz = 1 << p; int n, k; cin >> n >> k;
vector<base> A, B; vector<int> a(10, 0);
struct base { if(sz > (int)A.size()) A.resize(sz); while(k--) {
double x, y; for(int i = 0; i < (int)a.size(); i++) { int m; cin >> m;
base() { x = y = 0; } int x = (a[i] % mod + mod) % mod; a[m] = 1;
base(double x, double y): x(x), y(y) { } A[i] = base(x & ((1 << 15) - 1), x >> 15); }
}; } vector<int> ans = pow(a, n / 2);
inline base operator + (base a, base b) { return base(a.x + fill(A.begin() + a.size(), A.begin() + sz, base{0, 0}); int res = 0;
b.x, a.y + b.y); } fft(A, sz); for(auto x: ans) res = (res + 1LL * x * x % mod) % mod;
inline base operator - (base a, base b) { return base(a.x - if(sz > (int)B.size()) B.resize(sz); cout << res << ’\n’;
b.x, a.y - b.y); } if(eq) copy(A.begin(), A.begin() + sz, B.begin()); return 0;
inline base operator * (base a, base b) { return base(a.x * b.x else { }
- a.y * b.y, a.x * b.y + a.y * b.x); } for(int i = 0; i < (int)b.size(); i++) { //https://codeforces.com/contest/1096/problem/G
inline base conj(base a) { return base(a.x, -a.y); } int x = (b[i] % mod + mod) % mod;
int lim = 1; B[i] = base(x & ((1 << 15) - 1), x >> 15);
vector<base> roots = {{0, 0}, {1, 0}}; }
fill(B.begin() + b.size(), B.begin() + sz, base{0, 0});
vector<int> rev = {0, 1};
fft(B, sz);
7.10 Others
const double PI = acosl(- 1.0);
}
void ensure_base(int p) { Cycles Let gS (n) be the number of n-permutations whose cycle
if(p <= lim) return; double ratio = 0.25 / sz;
base r2(0, - 1), r3(ratio, 0), r4(0, - ratio), r5(0, 1); lengths all belong to the set S. Then
rev.resize(1 << p);
for(int i = 0; i < (1 << p); i++) rev[i] = (rev[i >> 1] >> 1) for(int i = 0; i <= (sz >> 1); i++) {
int j = (sz - i) & (sz - 1); ∞
+ ((i & 1) << (p - 1));
base a1 = (A[i] + conj(A[j])), a2 = (A[i] - conj(A[j])) *
X xn X xn
roots.resize(1 << p); gS (n) = exp
while(lim < p) { r2;
n=0
n! n∈S
n
double angle = 2 * PI / (1 << (lim + 1)); base b1 = (B[i] + conj(B[j])) * r3, b2 = (B[i] -
for(int i = 1 << (lim - 1); i < (1 << lim); i++) { conj(B[j])) * r4;
roots[i << 1] = roots[i]; if(i != j) { Derangements Permutations of a set such that none of the
double angle_i = angle * (2 * i + 1 - (1 << lim)); base c1 = (A[j] + conj(A[i])), c2 = (A[j] - conj(A[i])) * elements appear in their original position.
roots[(i << 1) + 1] = base(cos(angle_i), sin(angle_i)); r2;
} base d1 = (B[j] + conj(B[i])) * r3, d2 = (B[j] -
n!
lim++; conj(B[i])) * r4; D(n) = (n−1)(D(n−1)+D(n−2)) = nD(n−1)+(−1)n =
} A[i] = c1 * d1 + c2 * d2 * r5; e
} B[i] = c1 * d2 + c2 * d1;
void fft(vector<base> &a, int n = -1) { }
A[j] = a1 * b1 + a2 * b2 * r5;
Burnside’s lemma Given a group G of symmetries and a
if(n == -1) n = a.size(); set X, the number of elements of X up to symmetry equals
assert((n & (n - 1)) == 0); B[j] = a1 * b2 + a2 * b1;
int zeros = __builtin_ctz(n); }
fft(A, sz); fft(B, sz); 1 X
ensure_base(zeros);
vector<int> res(need); |X g |,
int shift = lim - zeros; |G| g∈G
for(int i = 0; i < n; i++) if(i < (rev[i] >> shift)) for(int i = 0; i < need; i++) {
swap(a[i], a[rev[i] >> shift]); long long aa = A[i].x + 0.5;
for(int k = 1; k < n; k <<= 1) { long long bb = B[i].x + 0.5; where X g are the elements fixed by g (g.x = x).
for(int i = 0; i < n; i += 2 * k) { long long cc = A[i].y + 0.5;
res[i] = (aa + ((bb % mod) << 15) + ((cc % mod) << 30))%mod; If f (n) counts “configurations” (of some sort) of length n, we
for(int j = 0; j < k; j++) {
} can ignore rotational symmetry using G = Zn to get
base z = a[i + j + k] * roots[j + k];
a[i + j + k] = a[i + j] - z; return res;
a[i + j] = a[i + j] + z; } n−1
1 X 1X
} g(n) = f (gcd(n, k)) = f (k)ϕ(n/k).
} vector<int> pow(vector<int>& a, int p) { n k=0 n
k|n
} vector<int> res;
} res.emplace_back(1);
while(p) {
//eq = 0: 4 FFTs in total
if(p & 1) res = multiply(res, a); 7.11 Primitive Root
//eq = 1: 3 FFTs in total
PTIT.Nutriboost 20
// Finds prime numbers between a and b, using basic primes up 9.13 Totient 10.2 Discrete Distributions
to sqrt(b)
// a must be greater than 1. 10.2.1 Binomial distribution
vector<long long> seg_sieve(long long a, long long b) { long long totient(long long n) {
long long ant = a; if (n == 1) return 0; The number of successes in n independent yes/no experiments,
a = max(a, 3LL); long long ans = n; each which yields success with probability p is Bin(n, p), n =
vector<bool> pmap(b - a + 1); for (int i = 0; primes[i] * primes[i] <= n; ++i) { 1, 2, . . . , 0 ≤ p ≤ 1.
long long sqrt_b = sqrt(b); if ((n % primes[i]) == 0) {
for (int i = 0; i < num_p; ++i) { while ((n % primes[i]) == 0) n /= primes[i]; n
long long p = primes[i]; ans -= ans / primes[i]; p(k) = pk (1 − p)n−k
}
k
if (p > sqrt_b) break;
long long j = (a + p - 1) / p; }
for (long long v = (j == 1) ? p + p : j * p; v <= b; v += if (n > 1) { µ = np, σ 2 = np(1 − p)
p) { ans -= ans / n;
pmap[v - a] = true; } Bin(n, p) is approximately Po(np) for small p.
} return ans;
} } 10.2.2 First success distribution
vector<long long> ans;
if (ant == 2) ans.push_back(2); The number of trials needed to get the first success in indepen-
int start = a % 2 ? 0 : 1; dent yes/no experiments, each wich yields success with probabil-
for (int i = start, I = b - a + 1; i < I; i
if (pmap[i] == false)
+= 2) 10 Probability and Statistics ity p is Fs(p), 0 ≤ p ≤ 1.
ans.push_back(a + i);
return ans; 10.1 Continuous Distributions p(k) = p(1 − p)k−1 , k = 1, 2, . . .
}
10.1.1 Uniform distribution
1 2 1−p
vector<pair<int, int>> factor(int n) { µ= ,σ =
If the probability density function is constant between a and b p p2
vector<pair<int, int>> ans;
if (n == 0) return ans;
and 0 elsewhere it is U(a, b), a < b.
for (int i = 0; primes[i] * primes[i] <= n; ++i) { 1 10.2.3 Poisson distribution
if ((n % primes[i]) == 0) { b−a
a<x<b
f (x) = The number of events occurring in a fixed period of time t if these
int expo = 0; 0 otherwise
while ((n % primes[i]) == 0) { events occur with a known average rate κ and independently of
expo++; a+b 2 (b − a)2 the time since the last event is Po(λ), λ = tκ.
n /= primes[i]; µ= ,σ =
} 2 12
λk
ans.emplace_back(primes[i], expo); p(k) = e−λ , k = 0, 1, 2, . . .
} 10.1.2 Exponential distribution k!
}
The time between events in a Poisson process is Exp(λ), λ > 0. µ = λ, σ 2 = λ
if (n > 1) {
λe−λx x ≥ 0
ans.emplace_back(n, 1);
} f (x) = 10.3 Probability Theory
0 x<0
return ans;
}
1 2 1 Let X be a discrete random variable with probability pX (x)
} µ= ,σ = 2 of assuming the value P x. It will then have an expected value
λ λ
(mean) µ = E(X) =P x xpX (x) and variance σ 2 = V (X) =
10.1.3 Normal distribution E(X 2 ) − (E(X))2 = x (x − E(X))2 pX (x) where σ is the stan-
dard deviation. If X is instead continuous it will have a proba-
9.12 Totient Sieve Most real random values with mean µ and variance σ 2 are well bility density function fX (x) and the sums above will instead be
described by N (µ, σ 2 ), σ > 0. integrals with pX (x) replaced by fX (x).
Expectation is linear:
for (int i = 1; i < MN; i++) 1 (x−µ)2
−
phi[i] = i; f (x) = √ e 2σ 2
2πσ 2 E(aX + bY ) = aE(X) + bE(Y )
for (int i = 1; i < MN; i++)
if (!sieve[i]) // is prime If X1 ∼ N (µ1 , σ12 ) and X2 ∼ N (µ2 , σ22 ) then For independent X and Y ,
for (int j = i; j < MN; j += i)
phi[j] -= phi[j] / i; aX1 + bX2 + c ∼ N (µ1 + µ2 + c, a2 σ12 + b2 σ22 ) V (aX + bY ) = a2 V (X) + b2 V (Y ).
PTIT.Nutriboost 25
} else if(ty == 2) { tempSA[--cnt[0]] = SA[i]; * the number of paths from the initial state to all the other
iac.insert(s, -1); SA = tempSA; * states.
} else if(ty == 3) { } *
int ans = iac.match(s); * The overall complexity is O(n)
printf("%d\n", ans); vector <int> constructSA(string s) { * can be tested here:
fflush(stdout); int n = s.length(); https://www.urionlinejudge.com.br/judge/en/problems/view/1530
} else { vector <int> SA(n); * */
abort(); vector <int> RA(n);
} vector <int> tempRA(n); struct state {
} for (int i = 0; i < n; i++) { int len, link;
} RA[i] = s[i]; long long num_paths;
return 0; SA[i] = i; map<int, int> next;
} } };
for (int step = 1; step < n; step <<= 1) {
countingSort(SA, RA, step); const int MN = 200011;
countingSort(SA, RA, 0); state sa[MN << 1];
11.3 KMP int c = 0; int sz, last;
tempRA[SA[0]] = c; long long tot_paths;
for (int i = 1; i < n; i++) {
vi pi(const string& s) { if (RA[SA[i]] == RA[SA[i - 1]] && RA[SA[i] + step] void sa_init() {
vi p(sz(s)); == RA[SA[i - 1] + step]) sz = 1;
rep(i,1,sz(s)) { tempRA[SA[i]] = tempRA[SA[i - 1]]; last = 0;
int g = p[i-1]; else sa[0].len = 0;
while (g && s[i] != s[g]) g = p[g-1]; tempRA[SA[i]] = tempRA[SA[i - 1]] + 1; sa[0].link = -1;
p[i] = g + (s[i] == s[g]); } sa[0].next.clear();
} RA = tempRA; sa[0].num_paths = 1;
return p; if (RA[SA[n - 1]] == n - 1) break; tot_paths = 0;
} } }
return SA;
vi match(const string& s, const string& pat) { } void sa_extend(int c) {
vi p = pi(pat + ’\0’ + s), res; int cur = sz++;
rep(i,sz(p)-sz(s),sz(p)) vector<int> computeLCP(const string& s, const vector<int>& SA) { sa[cur].len = sa[last].len + 1;
if (p[i] == sz(pat)) res.push_back(i - 2 * int n = SA.size(); sa[cur].next.clear();
sz(pat)); vector<int> LCP(n), PLCP(n), c(n, 0); sa[cur].num_paths = 0;
return res; for (int i = 0; i < n; i++) int p;
} c[SA[i]] = i; for (p = last; p != -1 && !sa[p].next.count(c); p =
int k = 0; sa[p].link) {
for (int j, i = 0; i < n-1; i++) { sa[p].next[c] = cur;
if(c[i] - 1 < 0) sa[cur].num_paths += sa[p].num_paths;
11.4 Suffix Array continue; tot_paths += sa[p].num_paths;
j = SA[c[i] - 1]; }
k = max(k - 1, 0);
const int MAXN = 200005; while (i+k < n && j+k < n && s[i + k] == s[j + k]) if (p == -1) {
k++; sa[cur].link = 0;
const int MAX_DIGIT = 256; PLCP[i] = k; } else {
void countingSort(vector<int>& SA, vector<int>& RA, int k = 0) { } int q = sa[p].next[c];
int n = SA.size(); for (int i = 0; i < n; i++) if (sa[p].len + 1 == sa[q].len) {
vector<int> cnt(max(MAX_DIGIT, n), 0); LCP[i] = PLCP[SA[i]]; sa[cur].link = q;
for (int i = 0; i < n; i++) return LCP; } else {
if (i + k < n) } int clone = sz++;
cnt[RA[i + k]]++; sa[clone].len = sa[p].len + 1;
else sa[clone].next = sa[q].next;
cnt[0]++; sa[clone].num_paths = 0;
for (int i = 1; i < cnt.size(); i++) 11.5 Suffix Automation sa[clone].link = sa[q].link;
cnt[i] += cnt[i - 1]; for (; p!= -1 && sa[p].next[c] == q; p = sa[p].link) {
vector<int> tempSA(n); sa[p].next[c] = clone;
for (int i = n - 1; i >= 0; i--) /* sa[q].num_paths -= sa[p].num_paths;
if (SA[i] + k < n) * Suffix automaton: sa[clone].num_paths += sa[p].num_paths;
tempSA[--cnt[RA[SA[i] + k]]] = SA[i]; * This implementation was extended to maintain (online) the }
else * number of different substrings. This is equivalent to compute sa[q].link = sa[cur].link = clone;
PTIT.Nutriboost 27