Hello Codeforces!
Today I'll be writing about what I have learnt about combinatorics, which played, and, in my opinion, will still be playing a important role in both Codeforces and CP (short for competitive programming).
However, combinatorics is such a great subject that made that impossible for me to write it all in one blog. So, this is just the second blog, which is friendly to beginners. If you are interested, please, pay attention to this account and I'll give posts as series for a long term.
If you have found some mistakes in the text, or if you have some questions about the topic, please, leave a comment, and I'll check it weekly and reply. Also, if you find some grammar mistakes, a kind comment will be also welcomed.
Previous Blogs
Blogs in the future
If finished, I'll link them.
- Combinatorics (3)
- Bitmask
- Probabilities
Content
- Quick power
- Fermat's little theorem
- extend-gcd
- The multiplicative inverse of an integer
- Prework optimize
- homework
Part 1 — Quick power
How to calculate the value of $$$a^b\bmod p$$$?
You may write out this code easily:
long long power(long long a, long long b, long long p) {
long long res = 1;
while(b--) res = res * a % p;
return res;
}
Seems easy, right? This code has $$$\mathcal O(b)$$$ time complexity. But what if $$$1\leq a,b\leq 10^9$$$? Can you solve it?
Here, we use divide ans conquer to solve this:
According to this, we can write out a code easily:
long long powpow(long long a, long long b, long long p) {
if(b % 2 == 1) {
long long r = powpow(a, b / 2, p);
return r * r % p * a % p;
} else {
long long r = powpow(a, b / 2, p);
return r * r % p;
}
}
Now certainly the time complexity is $$$\mathcal O(\log b)$$$. But this code contains a recursion. Since the recursion is simple, we can get rid of recursion:
long long quickpow(long long a, long long b, long long p) {
long long res = 1, nowa = 1;
while(b) {
if(b % 2 == 1) res = res * nowa % p;
nowa = a * a % p;
a = nowa;
b /= 2;
}
return res;
}
Now I'll give a fast version of it without explaining, you can use it in the later implementaions.
long long quickpow(long long a, long long b, long long p) {
long long res = 1;
while(b) {
if(b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
The time complexity remains the same, but the constant is smaller now. If you still wonders why, you can wait for my bitmask post.
Exercise: Calculate $$$(a\times b) \bmod p$$$, $$$10^9 \leq p\leq 10^{18}$$$.
In C++, long long
varible is between $$$-2^{63}$$$ and $$$2^{63}-1$$$, which is about $$$10^{18}$$$. When you do a * b % p
, then things below will happen:
- $$$a\times b$$$ is calculated first, it can overflow and lead to error.
- $$$a\times b\bmod p$$$ is calculated, the result is already wrong.
So multiply it with brute force is impossible, can you think of an $$$\mathcal O(\log b)$$$ solution?
You may wonder, if we divide it into $$$2$$$ parts, why don't we divide it into $$$k$$$ parts?
Actually, the time complexity is $$$\mathcal O(k\times\log_{k} n)$$$.
Look at the image we draw out $$$y=k+k\times \log_{k}n$$$:
And we draw out $$$\color{green} {x=2}$$$ and $$$\color{red}{x=3}$$$ when $$$n$$$ is very large:
So we find that $$$k=2,3$$$ is optimal. But $$$k=3$$$ uses $$$3$$$ if
sentences, this lead to larger constant than expected, so we use $$$k=2$$$ instead.
Part 2 — Fermat's little theorem
Here we start with a simple lemma, that is given here without a proof:
Then we have $$$a^{p-2}\equiv \frac{1}{p}\pmod p$$$, so we can do modulus operation to all rational numbers, since $$$\frac{p}{q}=\frac 1q \times p$$$.
Then you may find that we can do division operation modulus a number in $$$\mathcal O(\log p)$$$ time with quick power!
long long div(long long d, long long p) {
return quickpower(d, p - 2, p);
}
This is very important, since many problems requires you to output the result modulus $$$998244353$$$ or $$$10^9+7$$$, and they are all primes.
Now you can try to calculate single combination in $$$\mathcal O(n\log p)$$$ time.
Part 3 — Extend-gcd
First, let's review the Euclidean algorithm to calculate gcd:
int gcd(int a, int b) { if(a == 1) return b; return gcd(b % a, a); }
For example:
- $$$\gcd(66,26)$$$
- $$$66\div 26=2\cdots\cdots 14$$$
- $$$26\div14=1\cdots\cdots 12$$$
- $$$14\div12=1\cdots\cdots 2$$$
- $$$12\div2=6\cdots\cdots 0$$$
- $$$\therefore\gcd(66,26)=2$$$
Let's undo the progress:
- $$$\gcd(66,26)=2$$$
- $$$14=66-26\times 2$$$
- $$$12=26-14\times 1$$$
- $$$2=14-12\times 1$$$
- $$$0=12-2\times 6$$$
So easily we get:
$$$2=14-12\times 1$$$
$$$\;\;\;=14-(26-14\times 1)\times 1$$$
$$$\;\;\;=(66-26\times 2)-(26-(66-26\times 2)\times 1)\times 1$$$
$$$\;\;\;=2\times 66+(-5)\times 26$$$
Then you find that, we get a particular solution of the equation $$$ax+by=\gcd(a,b)$$$ when $$$a,b$$$ is given. This progress is called Extend Euclidean algorithm, also Extend GCD. For example, when $$$a=66,b=26$$$, one of the possible solutions is $$$(x,y)=(2,-5)$$$.
Ok, then we can write out the algorithm:
long long exgcd(long long a, long long b, long long &x, long long &y){
if(b == 0){
x = 1;
y = 0;
/*
* When b = 0, ax = gcd(a, 0) = a. So x = 1.
* The func returns the value of gcd, since a, b is not connected to x, y.
* But x, y is connected to a, b.
* Some also writes exgcd in the type of void,
* But in both versions,
* your varible x and y will be turned into one of the solutions of the equation.
*/
return a;
}
long long res = exgcd(b, a % b, y, x);
y -= (a / b * x);
return res;
}