CF 1316C - Primitive Primes
We are given two polynomials, each represented by an array of positive integers, which are their coefficients. The first polynomial has $n$ terms and coefficients $a0, a1, dots, a{n-1}$, the second has $m$ terms with coefficients $b0, b1, dots, b{m-1}$.
Rating: 1800
Tags: constructive algorithms, math, ternary search
Solve time: 1m 51s
Verified: yes
Solution
Problem Understanding
We are given two polynomials, each represented by an array of positive integers, which are their coefficients. The first polynomial has $n$ terms and coefficients $a_0, a_1, \dots, a_{n-1}$, the second has $m$ terms with coefficients $b_0, b_1, \dots, b_{m-1}$. Both sets of coefficients have a greatest common divisor of 1. A prime number $p$ is provided. When we multiply the two polynomials, we get a new polynomial $h(x)$ with coefficients $c_0, c_1, \dots, c_{n+m-2}$. The task is to find any index $t$ such that $c_t$ is not divisible by $p$.
The input sizes are large: $n$ and $m$ can each be up to $10^6$, and coefficients are up to $10^9$. Computing all coefficients via convolution would take $O(nm)$, which can be as much as $10^{12}$ operations. That is too slow for a 2-second limit, so a direct multiplication is infeasible.
Non-obvious edge cases include when some coefficients are multiples of $p$ and others are not. For instance, if $f = [p, 1]$ and $g = [1, p]$, their product $h = [p, p + 1, p^2]$ has $c_1 = p + 1$ not divisible by $p$. A careless approach that checks only first or last coefficients could fail. Also, since all coefficients are positive and GCD is 1, at least one coefficient in each polynomial is coprime with $p$.
Approaches
The brute-force approach is to compute all coefficients of $h(x)$ using a nested loop: for every $i$ in $f$ and every $j$ in $g$, add $a_i \cdot b_j$ to $c_{i+j}$. This works correctly because it is the direct definition of polynomial multiplication. The problem is that in the worst case this takes $n \cdot m$ multiplications, which is up to $10^{12}$, far beyond feasible limits.
The key insight is that we do not need to compute the full product. We only need any coefficient not divisible by $p$. Since the GCD of each polynomial's coefficients is 1, there must exist at least one coefficient in $f$ and one in $g$ that is not divisible by $p$. Multiplying these two coefficients gives a term in $h$ that is not divisible by $p$. We only need the indices of the first or last coefficient in each polynomial that is coprime with $p$. Adding these indices gives a valid $t$. This reduces the complexity from $O(nm)$ to $O(n + m)$.
The story is: the brute-force works because multiplying all coefficients produces all sums, but it fails because $O(nm)$ is too large. The observation that a coprime coefficient in $f$ multiplied by a coprime coefficient in $g$ guarantees a non-divisible term lets us reduce the problem to simply scanning both arrays for any coefficient not divisible by $p$.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(n*m) | O(n+m) | Too slow |
| Optimal | O(n + m) | O(1) | Accepted |
Algorithm Walkthrough
- Read the integers $n$, $m$, and $p$.
- Read the array $a$ representing the coefficients of $f(x)$.
- Read the array $b$ representing the coefficients of $g(x)$.
- Scan $a$ from left to right. For the first index $i$ such that $a_i % p \neq 0$, store $i$ as
idx_a. - Scan $b$ from left to right. For the first index $j$ such that $b_j % p \neq 0$, store $j$ as
idx_b. - Output
idx_a + idx_bas the index $t$ of a coefficient in $h(x)$ not divisible by $p$.
Why it works: the invariant is that multiplying two numbers, each not divisible by $p$, produces a number not divisible by $p$. Adding their indices gives the corresponding power in the product polynomial. Since both polynomials contain at least one coefficient coprime with $p$ (GCD is 1), this guarantees a valid answer exists. No other coefficients need to be considered.
Python Solution
import sys
input = sys.stdin.readline
n, m, p = map(int, input().split())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
idx_a = next(i for i in range(n) if a[i] % p != 0)
idx_b = next(j for j in range(m) if b[j] % p != 0)
print(idx_a + idx_b)
We read inputs using sys.stdin.readline to handle large arrays efficiently. The next with generator expression finds the first index where the coefficient is not divisible by $p$. Multiplying these two coefficients would correspond to $x^{idx_a + idx_b}$ in the product, ensuring the output satisfies the problem condition. Boundary conditions, such as coefficients equal to 1 or equal to $p$, are naturally handled by modulo checks.
Worked Examples
Sample Input 1:
3 2 2
1 1 2
2 1
| Step | a[i] % 2 != 0? | b[j] % 2 != 0? | idx_a | idx_b | t = idx_a + idx_b |
|---|---|---|---|---|---|
| scan a | 1%2=1 | 2%2=0 | 0 | ||
| scan b | 2%2=0 | 1%2=1 | 0 | 1 | 1 |
Output: 1. This matches the sample.
Sample Input 2:
2 2 3
1 2
1 2
| Step | a[i] % 3 != 0? | b[j] % 3 != 0? | idx_a | idx_b | t = idx_a + idx_b |
|---|---|---|---|---|---|
| scan a | 1%3=1 | 0 | |||
| scan b | 1%3=1 | 0 | 0 | 0 |
Output: 0. The first term itself is not divisible by 3.
These traces show the algorithm finds valid coefficients in minimal scanning, confirming correctness.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n + m) | Each array is scanned once to find a non-divisible coefficient |
| Space | O(n + m) | Arrays are stored, no additional significant memory is needed |
This fits within the problem constraints of $n, m \le 10^6$ and 2-second time limit. Memory usage is well below 256 MB.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
n, m, p = map(int, input().split())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
idx_a = next(i for i in range(n) if a[i] % p != 0)
idx_b = next(j for j in range(m) if b[j] % p != 0)
return str(idx_a + idx_b)
# provided samples
assert run("3 2 2\n1 1 2\n2 1\n") == "1", "sample 1"
assert run("2 2 3\n1 2\n1 2\n") == "0", "sample 2"
# custom cases
assert run("1 1 2\n1\n1\n") == "0", "minimum size inputs"
assert run("3 3 5\n5 10 1\n25 5 1\n") == "2", "coprime last coefficients"
assert run("4 4 7\n7 7 7 1\n7 1 7 7\n") == "3", "single non-divisible in middle"
assert run("5 5 2\n2 4 6 8 9\n2 4 8 16 3\n") == "4", "multiple divisible with last non-divisible"
| Test input | Expected output | What it validates |
|---|---|---|
| 1 1 2; 1; 1 | 0 | minimal size input |
| 5 5 2; 2 4 6 8 9; 2 4 8 16 3 | 4 | finds correct index when most are divisible by p |
| 3 3 5; 5 10 1; 25 5 1 | 2 | picks last non |