Skip to content

Commit 6583592

Browse files
authored
Merge pull request #2271 from cpinitiative/CF-718C
Solution for CF-718C
2 parents a7463c2 + 3ab8ecb commit 6583592

File tree

2 files changed

+203
-12
lines changed

2 files changed

+203
-12
lines changed

content/5_Plat/Matrix_Expo.problems.json

+24-12
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,30 @@
4242
"section": "23.3"
4343
}
4444
},
45+
{
46+
"uniqueId": "cf-691E",
47+
"name": "Xor-sequences",
48+
"url": "https://codeforces.com/contest/691/problem/E",
49+
"source": "CF",
50+
"difficulty": "Easy",
51+
"isStarred": false,
52+
"tags": ["Exponentiation", "Matrix"],
53+
"solutionMetadata": {
54+
"kind": "internal"
55+
}
56+
},
57+
{
58+
"uniqueId": "cf-718C",
59+
"name": "Sasha and Array",
60+
"url": "https://codeforces.com/problemset/problem/718/C",
61+
"source": "CF",
62+
"difficulty": "Normal",
63+
"isStarred": false,
64+
"tags": ["Exponentiation", "Matrix", "PURS"],
65+
"solutionMetadata": {
66+
"kind": "internal"
67+
}
68+
},
4569
{
4670
"uniqueId": "baltic-07-ConnectedPoints",
4771
"name": "2007 - Connected Points",
@@ -55,18 +79,6 @@
5579
"url": "https://github.com/mostafa-saad/MyCompetitiveProgramming/blob/master/Olympiad/Baltic/Baltic-07-Points.txt"
5680
}
5781
},
58-
{
59-
"uniqueId": "cf-691E",
60-
"name": "Xor-sequences",
61-
"url": "https://codeforces.com/contest/691/problem/E",
62-
"source": "CF",
63-
"difficulty": "Easy",
64-
"isStarred": false,
65-
"tags": ["Exponentiation", "Matrix"],
66-
"solutionMetadata": {
67-
"kind": "internal"
68-
}
69-
},
7082
{
7183
"uniqueId": "balkan-09-reading",
7284
"name": "2009 - Reading",

solutions/cf-718C.mdx

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
---
2+
id: cf-718C
3+
source: CF
4+
title: Sasha and Array
5+
author: Dustin Miao
6+
---
7+
8+
## Explanation
9+
10+
Firstly, a note on notation: let $F_i$ denote the $i$th Fibonacci number. That is,
11+
12+
$$
13+
F_i =
14+
\begin{cases}
15+
0 & i = 0 \\
16+
1 & i = 1 \\
17+
F_{i - 1} + F_{i - 2} & i \geq 2 \\
18+
\end{cases}
19+
$$
20+
21+
In this problem, we will build a segment tree over the array. For a leaf node in the segment tree with value $v$, we will store a pair of values $(F_{v - 1}, F_v)$. For all non-leaf nodes, we will store a pair of values equal to the pair-sum of its children. That is, if a node $u$ has children $v$ and $w$, then $$u = (v_0 + w_0, v_1 + w_1)$$.
22+
23+
We will use the term _cycle by k_ to denote transforming some pair $(F_{i - 1}, F_i)$ to $(F_{i - 1 + k}, F_{i + k})$. By default, the term _cycle_ refers to _cycle by 1_.
24+
25+
For each update, we need to cycle each leaf node by some amount $x$ such that the value in a node affected goes from $(F_{v - 1}, F_v)$ to $(F_{v - 1 + x}, F_{v + x})$. We can do this using matrix exponentiation.
26+
27+
First note that for a matrix $\begin{pmatrix} F_{i - 1} & F_i \end{pmatrix}$, we can cycle it by multiplying by $\begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix}$. That is,
28+
29+
$$
30+
\begin{pmatrix} F_i & F_{i + 1} \end{pmatrix} = \begin{pmatrix} F_{i - 1} & F_i \end{pmatrix}\begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix}
31+
$$
32+
33+
Inductively, we can cycle by $k$ by multiplying multiple times:
34+
35+
$$
36+
\begin{pmatrix} F_{i - 1 + k} & F_{i + k} \end{pmatrix} = \begin{pmatrix} F_{i - 1} & F_i \end{pmatrix}\begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix}^k
37+
$$
38+
39+
We can use binary exponentiation to quickly compute powers of the two-by-two matrix.
40+
41+
Secondly, we need to utilize the distributive property of matrix multiplication to same-size matrices. That is, if $m_k$ is a one-by-two matrix for $k \in [1, n]$, then the following property is satisfied:
42+
43+
$$
44+
(m_1 + m_2 + \dots + m_n) \cdot \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} = m_1 \cdot \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} + m_2 \cdot \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} + \dots + m_n \cdot \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix}
45+
$$
46+
47+
In that way, we can utilize lazy propagation on the segment tree. We will store an integer tag in each node denoting the lazy update, which denotes that we must cycle every node in its subtree by the value of the tag. When propagating, we can simply update the node by binary exponentiating and then multiplying, then storing it in the lazy tag. Querying works as normal on the segment tree.
48+
49+
## Implementation
50+
51+
<LanguageSection>
52+
53+
<CPPSection>
54+
55+
```cpp
56+
#include <bits/stdc++.h>
57+
using namespace std;
58+
59+
#define f first
60+
#define s second
61+
62+
using ll = long long;
63+
using pll = pair<ll, ll>;
64+
65+
const int MAXN = 1e5 + 1;
66+
const ll MOD = 1e9 + 7;
67+
68+
69+
//BeginCodeSnip{Matrix Operations}
70+
using Matrix = array<array<ll, 2>, 2>;
71+
72+
// multiplies two two-by-two matricies
73+
Matrix multiply(const Matrix &a, const Matrix &b) {
74+
return {(a[0][0] * b[0][0] + a[0][1] * b[1][0]) % MOD,
75+
(a[0][0] * b[0][1] + a[0][1] * b[1][1]) % MOD,
76+
(a[1][0] * b[0][0] + a[1][1] * b[1][0]) % MOD,
77+
(a[1][0] * b[0][1] + a[1][1] * b[1][1]) % MOD};
78+
}
79+
80+
// multiplies a one-by-two matrix with a two-by-two matrix
81+
pll multiply(const pll &a, const Matrix &b) {
82+
return {(a.f * b[0][0] + a.s * b[1][0]) % MOD,
83+
(a.f * b[0][1] + a.s * b[1][1]) % MOD};
84+
}
85+
86+
// returns {0, 1, 1, 1,} to the power of n
87+
Matrix pow(ll n) {
88+
Matrix res = {1, 0, 0, 1}; // identity matrix
89+
Matrix base = {0, 1, 1, 1}; // fibonacci matrix
90+
for (; n; n >>= 1) { // binary exponentiation (log n)
91+
if (n & 1) {
92+
res = multiply(res, base);
93+
}
94+
base = multiply(base, base);
95+
}
96+
return res;
97+
}
98+
//EndCodeSnip
99+
100+
int N, Q;
101+
pll tree[MAXN * 4];
102+
ll lazy[MAXN * 4];
103+
104+
// returns the pair-sum of a and b
105+
pll merge(const pll &a, const pll &b) {
106+
return {(a.f + b.f) % MOD, (a.s + b.s) % MOD};
107+
}
108+
109+
// pushes lazy update in t to its children
110+
void pushdown(int t) {
111+
if (lazy[t] == 0)
112+
return;
113+
tree[t << 1] = multiply(tree[t << 1], pow(lazy[t]));
114+
lazy[t << 1] += lazy[t];
115+
tree[t << 1 | 1] = multiply(tree[t << 1 | 1], pow(lazy[t]));
116+
lazy[t << 1 | 1] += lazy[t];
117+
lazy[t] = 0;
118+
}
119+
120+
// cycle range from l to r by v
121+
void update(int l, int r, ll v, int t = 1, int tl = 1, int tr = N) {
122+
if (r < tl || tr < l) {
123+
return;
124+
}
125+
if (l <= tl && tr <= r) {
126+
tree[t] = multiply(tree[t], pow(v));
127+
lazy[t] += v;
128+
return;
129+
}
130+
pushdown(t);
131+
132+
int tm = (tl + tr) >> 1;
133+
update(l, r, v, t << 1, tl, tm);
134+
update(l, r, v, t << 1 | 1, tm + 1, tr);
135+
tree[t] = merge(tree[t << 1], tree[t << 1 | 1]);
136+
}
137+
138+
// query sum from l to r
139+
ll query(int l, int r, int t = 1, int tl = 1, int tr = N) {
140+
if (r < tl || tr < l) {
141+
return 0;
142+
}
143+
if (l <= tl && tr <= r) {
144+
return tree[t].f;
145+
}
146+
pushdown(t);
147+
148+
int tm = (tl + tr) >> 1;
149+
return (query(l, r, t << 1, tl, tm) + query(l, r, t << 1 | 1, tm + 1, tr)) % MOD;
150+
}
151+
152+
int main() {
153+
cin >> N >> Q;
154+
fill_n(tree, MAXN * 4, pll{0, 1});
155+
for (int i = 1; i <= N; i++) {
156+
ll a;
157+
cin >> a;
158+
update(i, i, a);
159+
}
160+
161+
while (Q--) {
162+
int t;
163+
cin >> t;
164+
if (t == 1) {
165+
int l, r;
166+
ll v;
167+
cin >> l >> r >> v;
168+
update(l, r, v);
169+
} else if (t == 2) {
170+
int l, r;
171+
cin >> l >> r;
172+
cout << query(l, r) << '\n';
173+
}
174+
}
175+
}
176+
```
177+
</CPPSection>
178+
179+
</LanguageSection>

0 commit comments

Comments
 (0)