Thanks everybody for participating in the round!
You are free to leave feedback in the comments as well!
A. Square?
Author: JahonaliX Preparation: khba
# author: khba
for _ in range(int(input())):
a, b, c, d = map(int, input().split())
print('YES' if a == b == c == d else 'NO')
B. Your name
Author: khba Preparation: khba
// author: khba
#include <iostream>
using namespace std;
int cnt1[26], cnt2[26];
int main()
{
int t;
cin >> t;
while (t--)
{
for (int i = 0; i < 26; ++i) cnt1[i] = cnt2[i] = 0;
int n;
cin >> n;
string s, t;
cin >> s >> t;
for (char &c : s) cnt1[c - 'a'] ++;
for (char &c : t) cnt2[c - 'a'] ++;
bool answer = true;
for (int i = 0; i < 26; ++i)
if (cnt1[i] != cnt2[i])
answer = false;
puts(answer ? "YES" : "NO");
}
}
C. Isamatdin and His Magic Wand!
Author: Isamatdin Preparation: Isamatdin
// author: khba
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
bool odd = false, even = false;
int a[n];
for (int i = 0, x; i < n; ++i) {
cin >> a[i];
if (a[i] % 2 == 0) even = true;
else odd = true;
}
if (odd and even) sort(a, a + n);
for (int i = 0; i < n; ++i) cout << a[i] << " \n"[i == n - 1];
}
}
D. Yet Another Array Problem
Author: Nasa Preparation: Muhammadali__ & Nasa
Answer is always prime number, try to prove why.
Notice how equation works if $$$x$$$ does not satisfy property.
It means that for each $$$i$$$, $$$a_{i} \equiv 0 \ (mod \ x)$$$
// Author: BehruzbekX
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
using ll = long long;
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<ll> a(n);
for (auto &i: a) cin >> i;
for (ll x : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53}) {
int ok = 0;
for (ll i : a) {
if (i % x) {
ok = 1;
break;
}
}
if (ok) {
cout << x << '\n';
break;
}
}
}
}
E. khba Loves to Sleep!
Author: Isamatdin Preparation: Isamatdin
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t--) {
int n, k, x;
cin >> n >> k >> x;
vector<int> a(n);
for (int &i : a) cin >> i;
a.push_back(-1e9);
a.push_back(1e9);
n += 2;
sort(a.begin(), a.end());
int l = 0, r = x + 1;
while (l + 1 < r) {
int m = (l + r) >> 1, f = 0;
a[0] = - m, a[n - 1] = x + m;
for (int i = 1; i < n; ++i) f += max(0, (a[i] - m) - (a[i - 1] + m) + 1);
if (f >= k) l = m;
else r = m;
}
a[0] = - l, a[n - 1] = x + l;
int j = 0;
for (int i = 1; i < n; i++)
for (j = max(j, a[i - 1] + l); j <= min((a[i] - l), x) && k; j++)
cout << j << ' ', k--;
cout << '\n';
}
return 0;
}
F. Tree, TREE!!!
Author: Nasa Preparation: Nasa
We don't need to know lowest common ancestor (LCA) to solve this problem. Think of subtree sizes to replace LCA.
Let $$$sz_{i} = $$$ subtree size of $$$i$$$ when root is $$$r$$$. In order for $$$i$$$ to be able to be in the $$$S_{r}$$$: $$$sz_{i} \geq k$$$ must hold.
Notice how instead of calculating the answer for each root, we can calculate contribution of $$$i$$$ when root is $$$1 \ldots n$$$.
// Author: BehruzbekX
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
using ll = long long;
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
vector<vector<int>> a(n);
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v, --u, --v;
a[u].emplace_back(v);
a[v].emplace_back(u);
}
ll ans = 0;
vector<int> sz(n, 1); // initial subtree sizes
auto dfs = [&] (auto &dfs, int v, int p) -> void{ // performing DFS in the tree
for (int u : a[v]) {
if (u != p) { // to avoid visiting parent again
dfs(dfs, u, v); // recursively visit subtree
sz[v] += sz[u]; // merge
}
}
};
/*
|S_1| = (sz[1] >= k) + (sz[2] >= k) + ... (sz[n] >= k) (root is 1)
|S_2| = (sz[1] >= k) + (sz[2] >= k) + ... (sz[n] >= k) (root is 2)
|S_3| = (sz[1] >= k) + (sz[2] >= k) + ... (sz[n] >= k) (root is 3)
... (root is i)
|S_n| = (sz[1] >= k) + (sz[2] >= k) + ... (sz[n] >= k) (root is n)
We should add them for each node (calculating contribution):
*/
dfs(dfs, 0, -1); // Arbitrary starting point
for (int i = 0; i < n; i++) {
if (n - sz[i] >= k) ans += sz[i];
if (sz[i] >= k) ans += n - sz[i];
}
cout << ans + n << '\n';
}
}
Find the answer for each $$$i$$$ ($$$|S_{1}|, |S_{2}|, |S_{3}|, \ldots , |S_{n}|$$$).
Let us have the answer for $$$dp_{v} = $$$ answer when root is $$$v$$$, let $$$u$$$ be the directly connected node to $$$v$$$. How $$$dp_{v}$$$ changes when we make root $$$u$$$ ($$$dp_{u}$$$)?
First, we calculate the answer for each $$$i$$$ (the subtree of $$$i$$$) and subtree sizes, where the starting point is arbitrary.
Let's go back to our hint. Suppose we already have the answer for $$$dp_{v}$$$.
To get the answer for $$$dp_{u}$$$, we need to understand how the answer changes based on the subtree of $$$u$$$ and the part outside it.
This technique is known as Rerooting. When we transition from $$$v$$$ to $$$u$$$, we divide the total contribution into two parts:
1. inside the subtree of $$$u$$$, and
2. outside the subtree of $$$u$$$.
If $$$n - sz_{u} \lt k$$$, then we should subtract that contribution from $$$dp_{v}$$$ — this corresponds to the part outside the subtree of $$$u$$$, because when $$$u$$$ becomes root, $$$v$$$ have no longer subtree of $$$u$$$ as a subtree, which implies that we need to remove contribution of it. As for the subtree of $$$u$$$: if $$$sz_{u} \lt k$$$, then the answer increases by one because $$$n \ge k$$$.
Finally, the transition becomes:
// Author: BehruzbekX
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
using ll = long long;
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
vector<vector<int>> a(n);
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v, --u, --v;
a[u].emplace_back(v);
a[v].emplace_back(u);
}
ll ans = 0;
vector<int> sz(n, 1), dp(n); // initial subtree sizes and dp[i] = |S_i|
auto dfs = [&] (auto &dfs, int v, int p) -> void{ // performing DFS in the tree
for (int u : a[v]) {
if (u != p) { // to avoid visiting parent again
dfs(dfs, u, v); // recursively visit subtree
dp[v] += dp[u]; // answer for subtree of v (merge)
sz[v] += sz[u]; // merge
}
}
dp[v] += sz[v] >= k;
};
dfs(dfs, 0, -1); // arbitrary starting point
auto reroot = [&] (auto &reroot, int v, int p) -> void{
for (int u : a[v]) {
if (u != p) {
int add = dp[v] - dp[u] - (n - sz[u] < k); // value of v other than subtree of u
dp[u] = add + (dp[u] + (sz[u] < k));
reroot(reroot, u, v); //continue rerooting
// Actually you don't need dp[v] for each subtree (because dp[u] cancels out), I did it for clarity.
}
}
};
reroot(reroot, 0, -1);
cout << accumulate(dp.begin(), dp.end(), 0ll) << '\n'; // print the answer
}
}
G. Mukhammadali and the Smooth Array
Author: Muhammadali__ Preparation: Muhammadali__
Think of longest non-decreasing sequence
In finding simple LNDS, all elements have the same "weights". But here we are given weights.
// author: khba
#include "bits/stdc++.h"
using namespace std;
int main()
{
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
int64_t a[n], c[n], dp[n];
for (int64_t &i : a) cin >> i;
for (int64_t &i : c) cin >> i;
for (int i = 0; i < n; ++i) dp[i] = c[i];
for (int i = 0; i < n; ++i)
for (int j = 0; j < i; ++j)
if (a[j] <= a[i]) dp[i] = max(dp[i], dp[j] + c[i]);
cout << accumulate(c, c + n, 0LL) - *max_element(dp, dp + n) << '\n';
}
}
Solve for $$$n \le 2 * 10^5$$$








Why did not you set $$$n \leq 2*10^5$$$ for task G?
Solution is quadratic.
which is easy to make $$$O(n\log n)$$$
How?
with a data structure like segment tree, I think.
thanku,I will have a try
plz let me know if it works.
It is just a LNDS problem.
You want to calculate $$$dp_i[a]$$$, the maximal cost of an increasing subsequence among the first $$$i$$$ values ending at value $$$a$$$. There are at most $$$i$$$ interesting values of $$$a$$$ (the first $$$i$$$ elements of the array), and furthermore, if $$$dp_i[b] \lt dp_i[a]$$$ for some $$$b \gt a$$$, then $$$dp_i[b]$$$ is useless, since you're better off taking the subsequence ending at $$$a$$$. So you're keeping at most $$$i$$$ values of $$$a$$$, and $$$dp_i$$$ is increasing. Therefore, a good way to store $$$dp$$$ is a sorted list of pairs $$$(a, dp_i[a])$$$.
When going from $$$i - 1$$$ to $$$i$$$, we must add $$$dp_i[v[i]] = dp_{i - 1}[a] + c[i]$$$, where $$$a$$$ is the largest value less than $$$v$$$, which is like a lower_bound and can be found efficiently. For all other values, $$$dp_i = dp_{i - 1}$$$. Finally, to keep the pairs increasing, you can naively remove the pair next to $$$(v[i], dp_i[v[i]])$$$ while it is smaller. Since each pair can be removed only once, you'll be doing it at most $$$n$$$ times in total.
It can be efficiently implemented with a set, see my submission
thank you very much! orz
where a is the largest value less than vshouldn't it be
largest value less than or equal to vbecause we want to include non-decreasing part of array in the dp?In French less than means less than or equal to and positive means non-negative ... As always, blame it on the English
Thanks for the idea. Here is an alternative implementation.
This is kind of like dynamic Pareto frontier maintenance, basically “maintain only states that are not strictly worse than another in all dimensions,” and you enforce global monotonicity of both dimensions by maintaining a ordered set of one dimension + local dominance pruning of the other dimension per operation, after correctly placing the element based on first-dimension-order via set insertion. It can be seen as a generalized and more powerful version of monotonic stack. Cool technique!
It can be solved for $$$ n \le 2 * 10^5 $$$, however we decided, that there is no huge difference, so we chose the easier version
so you are the reason for the teleports , hmph!
hahaha, yeah
bro thank you for the easier version finally rached specialist , F was the most interesting
Congrats!
I am really glad that u achieved spec in our contest :)
I think that the contest is quite good, but I'm spec so I can't compete ;)) anyway, tks for the div 4
if you pupil you will cheat in this contest to increase rating :)))
i dont think so ;))
I know you're lying but I don't want to tell that
bro im not lying ;))
haha
You cheated in Codeforces Round 1051 (Div. 2), 1054 (Div. 3), 1060 (Div. 2)
You named the variable so much long and that's chatgpt
could u plz give some specific submissions as evidence? I just found that the gap of his performance in every contest is so weird.
340490950, 339123152 problem C was graph, G was tree he cheated
hii khba
How can it be done for n<=2e5?
hii, you can use any data structure that supports point update, and getting maximum on prefix.
In editorial you are finding element less than a[i] with maximum dp, so you can simply compress all numbers and get max dp value of elements in range $$$[0, a[i]]$$$, after u must update a[i] with the current value.
You can see my submission as well: 347018821
oh i think i get it now. Yes, Thank you.
hey, why can't I see ur sub?
What should Output for test case :
for E. khba Loves to Sleep! My accepted code gives 0 1 2 3 but i think this should 0 1 2 5
Both are correct
why ? 0 1 2 5 should correct because 5 has 1 minimum distance but 3 hasn't. so second one is more optimal
You have to consider the minimum time travelled by any person.
For 2,4,3 : 0 1 2 3 -> The guy at position 3 requires 0 seconds. 0 1 2 5 -> The guy at position 2 requires 0 seconds as there is a teleporter on position 2 , so they both are equivalent.
In this case you have to place 4 teleporters and x=5 so you have no way of achieving a better answer.
No see for 0 1 2 3 output we have - Minimal times for friends are [2,1,0,0], respectively. and for 0 1 2 5 output we have - Minimal times for friends are [2,1,0,1], respectively. so last one (for 5) have better answer.
Yes you are right. But you need to maximize of minimum of Minimal times. Example minimum of [2, 1, 0, 0] is 0. And minimum of [2, 1, 0, 1] is 0. So they are same. Because you see only minimum time instead of all times.
I agree.
ok i'm understood now. why i'm so dumb to not to understand this small thing !!
The problem wants you to maximise the time required by the first guy to reach a teleporter, which means you need to maximise the minimum, the rest are inconsequential.
AK the contest!, Thanks for this interesting contest
editorial for problem -G is pretty clever and simple!
thanks!
Figured a greedy approach for problem E choose the value with the maximum possible min distance from each $$$a_i$$$.
A simple proof to see it works is — say we have an optimal set $$$k$$$ which does not include point with the max distance say $$$k_m$$$, then we can simply replace any point in $$$k$$$ with $$$k_m$$$ and we still have an optimal set. This works for every possible stage.
Binary search gives the closest distance from a given position, we can use that as the cmp for a priority queue. Another greedy choice, rather than inserting every point in range [0,x], insert 0, x, and the midpoint(s) of $$$a_i$$$ and $$$a_{i-1}$$$ for $$$(1≤i≤n-1)$$$ (0-indexed) since one of those points have the max distance.
While we still have elements left to choose, pick the top priority say $$$c$$$, add to the result and then insert $$$c+1$$$ and $$$c-1$$$ as long as they are valid meaning in range [0,x] and not already chosen (my submission conveniently skipped this check, but still AC'd).
https://mirror.codeforces.com/contest/2167/submission/346322280
346321899 I have also done with greedy.
Hey Maroon5_sugar,
I guess i implemented smth similar to you. but my code fails. Could you help with it? 347070055
Your code misbehave when there are multiple friends at the same position. Removing any duplicates and keeping everything the same gets AC. 347099235. I believe it has to do with the multiple checks you're doing in the firs loop.
Feel free to take a look at my submission here, it's pretty similar.
oh yeah did not think of that point. I thought them friends would be at distinct places.. Hmm.. Thank you buddy.. really appreciate it!
I was also doing this but couldn't implement in time. Would you like to have a look at my wrong answer submission?
Why does the answer to E need to add n?
In the problem F, don't we have to consider the case when the number of nodes outside of node v + number of nodes in one of the subtree of node v >= k, then when the nodes in the remaining subtrees of v becomes a root, node v contributes to the answer, right? it is not covered in the editorial, or did I miss anything?
After seeing your question, I had the same doubt. Later I realized that this answer is included in the calculation of v's child nodes. When sz[v] >= k, the contribution of v to the n-sz[v] nodes outside v's subtree is added. When n-sz[v] >= k, the contribution of f[v] to the sz[v] nodes within v's subtree is added. In other words, when traversing each v from 1 to n, the calculation includes v's contribution to nodes outside its subtree and f[v]'s contribution to nodes within its subtree, but the contribution of v itself is not accounted for in either v's calculation or its child nodes' calculations. This is why we need to add n to the ans when outputting the result.
看到你的提问之后我也产生了相同的疑惑,后来我发现这种答案包含在v的子结点的计算中,sz[v]>=k时,将v对除v子树之外n-sz[v]个结点的贡献加入了,n-sz[v] >= k时,将f[v]对v子树中sz[v]个结点的贡献加入了,也就是说在从1到n遍历每一个v时,计算了v对v子树之外结点和f[v]对v子树中结点的贡献,而对v本身的贡献无论在v的计算中还是在v子结点的计算中都没有计入,这也是为什么要在输出时给ans加n
Got it! Thank you for the clarification
The prime enumeration in the solution for Problem D missed 47.
Thanks, fixed.
Fast and well-written editorial. F is such a good and basic problem of root-changing DP!
can anybody tell me why we are adding n to the answer in the problem F?
That n accounts for making each of the nodes 1,2,...,n as the root. Since k is atmost n, the root r will be included (take one of the nodes as the root, and arbitrarily select rest of the k — 1 nodes).
When sz[v] >= k, the contribution of v to the n-sz[v] nodes outside v's subtree is added. When n-sz[v] >= k, the contribution of f[v] to the sz[v] nodes within v's subtree is added. In other words, when traversing each v from 1 to n, the calculation includes v's contribution to nodes outside its subtree and f[v]'s contribution to nodes within its subtree, but the contribution of v itself is not accounted for in either v's calculation or its child nodes' calculations. This is why we need to add n to the ans when outputting the result.
sz[v]>=k时,将v对除v子树之外n-sz[v]个结点的贡献加入了,n-sz[v] >= k时,将f[v]对v子树中sz[v]个结点的贡献加入了,也就是说在从1到n遍历每一个v时,计算了v对v子树之外结点和f[v]对v子树中结点的贡献,而对v本身的贡献无论在v的计算中还是在v子结点的计算中都没有计入,这也是为什么要在输出时给ans加n
i am wondering for that,thx a lot 爱你煲
Hi, can you please explain what is f[v] here?
Sorry for the late reply.f[v] means the father node of v.
What is F talking about? I read it once and once but havn't understand the explanation of test case yet.
Aside from the testing speed, everything else is good.
Hello can anyone help me regarding the problem G , i don't understand how this solution(editorial) guarantees correct ans is there any proof for eg what should be the ans for a={1,1,1,17,1,1,1},c={1,1,1,10000,1,1,1}
10003
why problem A tagged "2-sat"?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //this is the code
include <bits/stdc++.h>
using namespace std;
int main() { int t; cin >> t; while (t--) { int n, k; cin >> n; vector a(n); vector<pair<long long, int>> vpp(n); vector d(n);
for (int i = 0; i < n; i++) { cin >> a[i]; } for (int i = 0; i < n; i++) { cin >> d[i]; } long long sum = LLONG_MAX; long long check; long long e, t; for (int i = 0;i < n; i++) { check = 0; e = a[i]; t = a[i]; for (int j = i + 1; j < n; j++) { if (a[j] <e) { check = check + d[j]; } if (a[j] > e) { e = a[j]; } } for (int j = i - 1; j >= 0; j--) { if (a[j] >t) { check = check + d[j]; } if (a[j] < t) { t = a[j]; } } sum = min(sum, check); } cout << sum << endl; }} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Please tell me whats wrong in this solution for question G of the contest
F why ans+n ,i wonder why add n
as the condition $$$k \le n$$$ is always true, every root can the lca of some subset of nodes
I still can't understand the solution for problem E , help me coders.
Instead of thinking about k first, think that for an assumed minimum distance m, is it possible to fit at least k teleports on the number line. If it is true, we search the greater search space for greater (more likeable) values of m, if not we go and choose the lesser search space.
In the editorial for F, It says when outside the subtree of i, if n- szi >= k, then we can choose any root in subtree of i and szi >= k hold, I agree with that. But what about the cases when n — szi <= k , if I choose root as someone in its subtree and after that the size of the subtree of i becomes >= k ? Why are we not taking that case
"if I choose root as someone in its subtree and after that the size of the subtree of $$$i$$$ becomes $$$ \gt = k$$$" it will not, if I understood your problem corrently. I'm sorry if I misunderstood.
Think about it. How will it change? If you root differently from current rooting by choosing a node from a certain path's, the previous root's subtree will be the other unexplored/branched-out portion. In other words, if a tree starts like
.....1
/....|....\
2....3.....4
then, any new root chosen from 2's branch will have a fixed size subtree in node 1, and that size is always sz(3) + sz(4) + 1 (the node 1 itself).
Hope it helps. Again, not sure if this is what you were thinking about, but I tried.
yes exactly so what if 1 + sz(3) + sz(4) becomes >= k, but the solution will does not account for this condition, if only says n — sz(1) >= k
We actually need to check
n - sz(2) >= kif we are talking about my example. It means that for node 1, if node count except 2's path (thusn - sz(2)) is bigger than k, then all nodes in that subtree will have current node (node 1) as their answer if we make them root. Repeat that for all the paths, and repeat all of it for all current nodes.Problem F seems to be intuitively easier compared to problem G (because I didn't even think about reversing the DP as to finding max instead of plugging directly from the problem where I have to find min) but why is it the case that there seems to be much more people solving problem G instead of F?
it is also possible to solve G with finding minimum cost (346354583)
imo gpt could solve it easily
G is easy with n <= 8000. imo n <= 1e5 should have been a better constraint, and more fitting for this position.
https://mirror.codeforces.com/contest/2167/submission/346293181 what a d, my fellas
In problem E, we can do a different logic in the binary search.
It’s related to the union of the forbidden segments.
For each i, the forbidden segment is [a[i]−dif+1,a[i]+dif−1].
The union will be: for each left endpoint we take the maximum right endpoint, and then we create an array that doesn’t contain intersections between segments.
Then, we have forbidden segments [x1, x2], [x3, x4], [x5, x6],.. we take the teleports from the segments [x2+1,x3−1],[x4+1,x5−1]..
This is my submission 350253562
In the editorial of problem F, I think it should be better to state: if $$$n - sz_i \ge k$$$ holds, then $$$sz_{p_i} \ge k$$$ for roots in the subtree of $$$i$$$. (here $$$p_i$$$ is $$$i$$$'s parent in the rooted tree.)
i.e when $$$n - sz_i \lt k$$$ we might be missing contributions of $$$i$$$ but we are not missing contributions of $$$p_i$$$. So in the algorithm we are actually adding the parent's contributions.
For question G why this Top down approach fails ...irrespective of TLE or MLE...this fails because it gives wrong answer but how what is the flaw in this solution-->
hi. how am i supposed to know if 53 will be the number upto which the product of primes will exceed 10^18? is this a regular pattern?.(ps. sorry if the question seems dumb, im a newbie)
The guy in the video explains why. If you multiply all the prime numbers till 53 it can cover all numbers up to 10^18.
In cases like this, you can write a quick program to check. Shouldn't take any longer than a minute if you're comfortable with programming:)
instead of trying to find the exact number (53), can we just see numbers which contain 10 in them ? there are more 18 numbers which have 10 in them in the primes < 100. (11, 13, 17, ... 97), so checking for primes < 100 is enough.
ofcourse this is slower and increases the time complexity by a constant but a easier intuition.
In question D what must be the output for the array 6, 18, 30. I think it should be 4. But the according to solution it is 5. How? I think there is a glitch.
$$$gcd(4,30)=2$$$
$$$gcd(4,18)=2$$$
$$$gcd(4,6)=2$$$
Hope this helps!
Additionally, the answer cannot be composite, as for any composite number, there exists a prime that is a better choice.
question requires that atleast for one number in the array for which gcd(x, num) should be 1. [6, 18, 30] and x = 4 does not satisfy this.
if there is atleast one odd number in the array, answer must be 2 as gcd(2, odd) = 1
if every number in the array is even, then we must find the smallest odd number (to have gcd as 1). for every composite odd number, there is always a smaller prime number which also gives gcd as 1. so it comes down to finding the smallest prime number.
how many prime numbers to check for ? primes number < 100 are enough because