Hello everyone!
Cybros, the competitive programming club of LNMIIT Jaipur is back again to invite you to participate in Enigma 2025 — Cybros LNMIIT. Enigma is the flagship event of Cybros LNMIIT conducted in the LNMIIT Jaipur's annual tech fest Plinth.
This will be an individual programming contest and the duration will be 2.5 hours and you will be given 6 problems to solve.
- Registration Form : Form link
- Contest Link : Enigma '25
- Date : January 26, 2025 at 14:00 IST (8:30 UTC)
The prize pool of the contest is INR 22000 split into two divisions of participants, online and onsite. You must fill the registration form if you want to be considered for the prizes.
Prize pool for onsite participants :
- INR 10000 (Winner)
- INR 7000 (First runner up)
- INR 3000 (Second runner up)
Prize pool for online participants (only applicable for Indian school/college students):
- INR 2000 (Winner)
- Goodies (Top 5 participants)
The problems have been authored and tested by synthborne, paramveer5404, Punpun018, AyushK22, Overlord99.

Cybros LNMIIT is also conducting more Competitive Programming events in their tech fest. You can check the rest of the events in the brochure which might interest you.
As a part of Plinth, we will also conduct IUPC (Inter University Programming Contest), which is an ICPC-like contest for teams of three people. This contest is a good practice for the real ICPC rounds with cash prizes.
You can register for rest of the events from our Instagram and receive future updates regarding the events.
UPD1 : Contest registrations are open!
UPD2 : The contest starts in 30 minutes. Do register.
UPD3 : Top 5 participants of the contest :
First solves :
- A : arnabmanna
- B : arnabmanna
- C : 1_2_3_4_5_9
- D : Banis
- E : kingmessi
- F : 1_2_3_4_5_9
UPD4 : We are sorry for the delayed editorial.
EDITORIAL
Author : AyushK22, synthborne
Lets see when the answer is $$$0$$$. If two consecutive non missing elements are not following the condition i.e. $$$a_{i + 1} \leq a_{i}$$$. If two elements one to the left of a missing position, lets call is $$$a$$$ and one to the right, lets call it $$$b$$$ are not following the condition i.e. $$$b \leq a + 1$$$.
In any other scenario, the answer always exists. We can place $$$b - a - 1$$$ elements at each missing position ($$$a$$$ and $$$b$$$ as defined above) and multiply all these values to get the answer.
#include<bits/stdc++.h>
#define int long long
using namespace std;
const char nl = '\n';
const char _ = ' ';
void fn(){
int n; cin>>n;
int arr[n];
int M = 1e9 + 7;
for(int i = 0; i < n; i++) cin>>arr[i];
for(int i = 1; i < n; i++){
if(arr[i] != -1 && arr[i - 1] != -1){
if(arr[i] <= arr[i - 1]){
cout<<0<<endl;
return;
}
}
}
int p = 1;
for(int i = 1; i < n - 1; i++){
if(arr[i] == -1){
int d = arr[i + 1] - arr[i - 1] - 1;
if(d <= 0){
cout<<0<<endl;
return;
}
p = (p * 1LL * d) % M;
}
}
cout<<p<<endl;
}
signed main() {
cin.tie(nullptr)->sync_with_stdio(false);
int T; cin >> T;
for (int t=1; t<=T; ++t)
fn();
return 0;
}
Author : roronoa_2z
B : 580057B - Bitwise Treasure Hunt
If a bit is unset it remains unset.
Notice when a new AND value is added to the set.
The AND Sequence, though infinite, involves a decreasing operation. The solution employs a greedy approach: if a continuous segment of ones is set in the binary representation of the last number added to the set, they all become unset simultaneously. Thus, our approach is simple: set all consecutive segments of ones from right to left to $$$0$$$, and each time, add the resulting number to the set. Given that $$$n \le 10^{18} $$$, we have at most $$$\mathcal{O}(60)$$$ operations per test case, as there are a maximum of $$$60$$$ bits.
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef unsigned long long ull ;
void solve()
{
int n ;
cin >> n ;
bitset<65> bt1(n);
set<int> possible ;
int previ= 0 ;
for(int i =0 ;i<=64; i++ )
{
if( bt1[i] == 0 )
{
for(int j = i-1 ; j>=previ ;j-- )
{
bt1[j] = 0 ;
}
ull t2= bt1.to_ullong() ;
possible.insert(t2);
previ = i;
}
}
cout << possible.size() << "\n";
for( auto x: possible)
{
cout << x << " " ;
}
cout << "\n" ;
}
signed main(){
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int tt;
cin >> tt;
while(tt--)
solve();
return 0;
}
Author : synthborne
C : 580057C - Permutation Colouring
At most $$$k$$$ operations is equivalent to exactly $$$k$$$ operations here.
Think about number of adjacent value pairs $$$a_i \gt a_{i + 1}$$$ in the permutations.
Lets rephrase the problem first. If we can colour a permutation in less than $$$k$$$ operations, then we can always colour it in exactly $$$k$$$ operations. So, we will try to colour permutations in least number of operations possible. We will always select a maximal increasing subarray at any instance and colour it entirely. Now, the problem reduces down to finding minimum number of increasing subarrays we can split the permutation into, lets call it $$$x$$$ which can be coloured in $$$x$$$ operations.
We can see that if a permutation can be split into a minimum of $$$x$$$ increasing subarrays. It must have $$$x - 1$$$ number of adjacent value pairs where $$$a_i \gt a_{i + 1}$$$. Lets call these kinds of adjacent value pairs as "bad" pairs. So, we will try to find the number of permutations of length $$$n$$$ for each value of $$$j$$$ from $$$0$$$ to $$$k - 1$$$ where $$$j$$$ denotes the number of bad pairs.
We will use dynamic programming to do this. $$$dp(i)(j)$$$ stores the number of permutations of length $$$i$$$ with $$$j$$$ bad pairs. Now, if we want to place the element $$$i + 1$$$ to make permutations of length $$$i + 1$$$. We can place it as the last element of any $$$j + 1$$$ increasing subarrays to retain the number of bad pairs or place it anywhere else to increase the number of bad pairs by $$$1$$$.
- $$$dp(i)(j) = dp(i)(j) + dp(i-1)(j) \times (j+1)$$$
- $$$dp(i)(j) = dp(i)(j) + dp(i-1)(j-1) \times (i-j)$$$
So, for some $$$n$$$ and $$$k$$$. The answer becomes the sum of all $$$dp(n)(j)$$$ values for $$$0 \leq j \leq k - 1$$$, which can be accessed in $$$O(1)$$$ after taking prefix sums of the computed $$$dp$$$ table.
Time complexity : $$$O(nk)$$$
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
#define int long long
#define ll long long
#define ull unsigned long long
#define vi vector<int>
#define vll vector<long long>
#define pr pair<int, int>
#define vpi vector<pair<int,int>>
#define vpll vector<pair<ll,ll>>
#define mapi map<int,int>
#define mapll map<ll,ll>
#define all(x) x.begin(), x.end()
#define yes cout<<"YES\n"
#define no cout<<"NO\n"
const int M = 1e9 + 7;
int dp[5001][5001];
void solve(){
int N = 5000;
dp[1][0] = 1;
for(int i = 2; i <= N; i++){
for(int j = 0; j < i; j++){
dp[i][j] = (dp[i][j] + dp[i - 1][j] * (j + 1)) % M;
dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] * (i - j)) % M;
}
}
for(int i = 1; i <= N; i++){
for(int j = 1; j < i; j++){
dp[i][j] = (dp[i][j] + dp[i][j - 1]) % M;
}
}
int t; cin>>t;
for(int i = 0; i < t; i++){
int n, k;
cin>>n>>k;
cout<<dp[n][k - 1]<<endl;
}
}
signed main(){
std::ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while(t--){
solve();
}
return 0;
}
Author : synthborne
What about primes $$$p$$$ such that $$$ p \leq n / 3$$$.
What about primes $$$p$$$ such that $$$n / 3 \lt p \leq n / 2$$$.
If we can take a prime $$$p$$$ into the answer, there always exists a way to take all the multiple of $$$p$$$ into the answer. So, we should talk about what all primes we can take. From a first glance, it might seem that we can take all primes $$$p$$$ such that $$$p \leq n / 2$$$, but this is not the case.
If we want to place a prime $$$p$$$ in our construction which is not at the either ends of the array. We have to place non coprime values to its left and right, and the best we can do is to place the numbers $$$2p$$$ and $$$3p$$$ to its left and right. So, we can place any prime $$$p$$$ such that $$$p \leq n / 3$$$. A seemingly nice construction may look something like this : $$$2p_1$$$, $$$p_1$$$, $$$3p_1$$$, $$$3p_2$$$, $$$p_2$$$, $$$2p_2$$$, $$$2p_3$$$, $$$p_3$$$,...
If we are placing primes at the either ends of the array (only $$$2$$$ such positions exist). We will only need $$$1$$$ non coprime number beside it (there is no other side) and best we can do is to place $$$2p$$$. The construction looks something like this :
Left end : $$$p_1$$$, $$$2p_1$$$,...
Right end : ..., $$$2p_2$$$, $$$p_2$$$
So, we can take all primes $$$p$$$ such that $$$p \leq n / 3$$$ and at most $$$2$$$ primes such that $$$n / 3 \lt p \leq n / 2$$$. It is also always possible to take all the multiples of the taken primes into the construction, an exercise left to the readers.
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
#define int long long
#define ll long long
#define ull unsigned long long
#define vi vector<int>
#define vll vector<long long>
#define pr pair<int, int>
#define vpi vector<pair<int,int>>
#define vpll vector<pair<ll,ll>>
#define mapi map<int,int>
#define mapll map<ll,ll>
#define all(x) x.begin(), x.end()
#define yes cout<<"YES\n"
#define no cout<<"NO\n"
const int N = 1e6;
int sieve[N + 1];
vector<int> primes;
void solve(){
int n;
cin>>n;
vector<int> v;
int idx = 0;
for(int i = 0; i < primes.size(); i++){
if(primes[i] <= n / 3){
v.push_back(primes[i]);
}
else{
idx = i;
break;
}
}
if(primes[idx] <= n / 2) v.push_back(primes[idx]);
if(primes[idx + 1] <= n / 2) v.push_back(primes[idx + 1]);
int hash[n + 1]{0};
for(auto &i : v){
for(int j = i; j <= n; j += i) {
hash[j] = 1;
}
}
ll sum = 0;
for(int i = 1; i <= n; i++){
if(hash[i]) sum++;
}
cout<<sum<<endl;
}
signed main(){
std::ios_base::sync_with_stdio(false);
cin.tie(nullptr);
for(int i = 2; i <= N; i++){
if(sieve[i]) continue;
for(int j = 2 * i; j <= N; j += i){
sieve[j] = 1;
}
}
for(int i = 2; i <= N; i++){
if(!sieve[i]) primes.push_back(i);
}
int t = 1;
cin>>t;
while(t--){
solve();
}
return 0;
}
Author : paramveer5404, synthborne
We will first compute the information, the sequence of ranges $$$(l, r)$$$ in the order of rain blocks falling on them (from $$$r$$$ to $$$l$$$, $$$h$$$ times) increasing the height of the range by $$$h$$$ (maximum $$$h$$$ possible until the range changes). The number of such ranges will obviously be of $$$O(n)$$$.
We can easily find this by maintaining a monotonic stack of three variables, left end of the range, right end of the range and current height of this range. We will push a range $$$(l, r, cur_h)$$$ into our stack if the stack's current topmost range's height $$$h$$$ is larger than height of current range $$$cur_h$$$. Otherwise, it would mean that rain blocks will fall on current stack's topmost range now until its height $$$h$$$ becomes larger than $$$cur_h$$$. We can simulate this process by popping the ranges from the stack and performing necessary calculations of range attributes. This takes $$$O(n)$$$.
This gives us the sequence of ranges in the order of time of rain blocks falling on them. We can also find the total time it takes to fill a range $$$(l, r, h)$$$ which is equal to $$$(r - l + 1) \times h$$$. This may also give us the timestamp when rain blocks fall on any range by finding the prefix sum of time taken on each range in this sequence.
Sort the queries with respect to time $$$t$$$. We will answer the queries in ascending order of $$$t$$$. For some $$$t$$$, we need to simulate updates for every range which has already been acted upon by the rain blocks (whose ending times are lesser than $$$t$$$) by using a segment tree. For some range $$$(l, r, h)$$$, we can add $$$h$$$ into the range $$$(l, r)$$$ to simulate this.
For some query $$$(pos, t)$$$ and the range with largest start time $$$\leq t$$$ be $$$(l, r)$$$. There are two possible cases :
- If $$$pos$$$ is not inside $$$(l, r)$$$. We can simply return the answer from the segment tree, the value at $$$pos$$$.
- Otherwise, if $$$pos$$$ is inside $$$(l, r)$$$. We also need to simulate the current falling process on this range. So, we need to add the increase in height from this process in addition to the segment tree value at $$$pos$$$.
Time complexity : $$$O(nlogn)$$$
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
#define int long long
#define ll long long
#define ull unsigned long long
#define vi vector<int>
#define vll vector<long long>
#define pr pair<int, int>
#define vpi vector<pair<int,int>>
#define vpll vector<pair<ll,ll>>
#define mapi map<int,int>
#define mapll map<ll,ll>
#define all(x) x.begin(), x.end()
#define yes cout<<"YES\n"
#define no cout<<"NO\n"
const int N = 2e5 + 5;
int seg[4 * N];
void update(int v, int l, int r, int L, int R, int val){
if(L > R)
return;
if(L == l && R == r){
seg[v] += val;
return;
}
int mid = (l + r) / 2;
update(2 * v + 1, l, mid, L, min(R, mid), val);
update(2 * v + 2, mid + 1, r, max(mid + 1, L), R, val);
}
int query(int v, int l, int r, int pos) {
if(l == r){
return seg[v];
}
int mid = (l + r) / 2;
if(pos <= mid){
return seg[v] + query(2 * v + 1, l, mid, pos);
}
else{
return seg[v] + query(2 * v + 2, mid + 1, r, pos);
}
}
void solve(){
int n, q;
cin>>n>>q;
int arr[n + 1];
for(int i = 0; i < n; i++) cin>>arr[i];
arr[n] = -1;
int l = 0;
vector<pair<pr, int>> v;
for(int i = 1; i <= n; i++){
if(arr[i] != arr[i - 1]){
v.push_back({{l, i - 1}, arr[i - 1]});
l = i;
}
}
v.push_back({{n, n}, INT_MAX});
vector<pair<pr, int>> order;
stack<pair<pr, int>> st;
order.push_back({{0, 0}, 0});
//monotonic stack precomputation
for(int i = 0; i < v.size(); i++){
int l = v[i].first.first;
int r = v[i].first.second;
int h = v[i].second;
if(st.empty()){
st.push({{l, r}, h});
continue;
}
if(st.top().second < h){
int curl = st.top().first.first;
int curr = st.top().first.second;
int curh = st.top().second;
st.pop(); i--;
if(st.empty()){
if(i == v.size() - 2) break;
order.push_back({{curl, curr}, (curr - curl + 1) * abs(h - curh)});
st.push({{curl, curr}, h});
continue;
}
int prevl = st.top().first.first;
int prevr = st.top().first.second;
int prevh = st.top().second;
int val = min(abs(curh - prevh), abs(curh - h));
order.push_back({{curl, curr}, (curr - curl + 1) * val});
if(abs(curh - prevh) <= abs(curh - h)){
st.pop();
st.push({{prevl, curr}, prevh});
}
else{
st.push({{curl, curr}, h});
}
}
else if(st.top().second == h){
int curl = st.top().first.first;
st.pop();
st.push({{curl, r}, h});
}
else{
st.push({{l, r}, h});
}
}
// prefix array of range updates
for(int i = 1; i < order.size(); i++) {
order[i].second += order[i - 1].second;
}
for(int i = 0; i <= 4 * n; i++) seg[i] = 0;
int answer[q]; int p = 1;
vector<pair<pr, int>> queries;
for(int i = 0; i < q; i++){
int time, pos;
cin>>time>>pos; pos--;
queries.push_back({{time, pos}, i});
}
// offline queries
sort(all(queries));
for(int i = 0; i < q; i++){
int time = queries[i].first.first;
int pos = queries[i].first.second;
int idx = queries[i].second, l, r;
while(p < order.size() && order[p].second <= time){
l = order[p].first.first;
r = order[p].first.second;
int val = (order[p].second - order[p - 1].second) / (r - l + 1);
update(0, 0, n - 1, l, r, val);
p++;
}
if(p == order.size()){
int d = time - order[p - 1].second;
d = d - (n - pos) + n;
answer[idx] = arr[pos] + query(0, 0, n - 1, pos) + d / n;
continue;
}
int d = time - order[p - 1].second;
l = order[p].first.first;
r = order[p].first.second;
d = d - (r - pos + 1) + (r - l + 1);
if(l <= pos && pos <= r){
//in the range
answer[idx] = arr[pos] + query(0, 0, n - 1, pos) + d / (r - l + 1);
}
else{
// outside the range
answer[idx] = arr[pos] + query(0, 0, n - 1, pos);
}
}
for(int i = 0; i < q; i++) cout<<answer[i]<<" ";cout<<endl;
}
signed main(){
std::ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while(t--){
solve();
}
return 0;
}
Author : synthborne
F : 580057F - Everblossom Tree
Turns out the participants had much easier solutions than the author solution.
Coming soon...









Hello Yokoso Watashi no soul societyy!
How to register for iupc?
For that you can follow our Instagram page where we will share updates regarding all the other events.
PUNPUN FTW!!
As the coordinator Punpun018 's roommate i confirm that this is a great contest.
Auto comment: topic has been updated by synthborne (previous revision, new revision, compare).
Excited!!
can't wait for this one!! CYBROS FTW!!!
Lessgo!!
Can’t wait for this contest to kick off!
Lessgo!! Ready for the experience
As synthborne friend , ICPC teammate and fellow setter , I can confirm the problems are gr8 . Hope you enjoy the contest .
We have tried our best to create an amazing problemset for all of you to enjoy. Good luck to those willing to participate.
synthborne orz
This contest has an amazing problemset, excited for everyone to try it!
The problems were really great!! Thanks
Cool problems! How to solve D btw?
After thinking about it a bit, I came to the conclusion that all primes between n / 2 and n are never going to be part of the solution. By observation of about 20 cases and a pseudo proof(which I thought was right), I thought that ans = n — #of primes b/w n / 2 and n — 1. Why should this be wrong? What is the right approach?
what about the prime p such that
2*p <= n < 3*pAt most two such prime numbers are valid
Damn, thats true...
Because if we want to insert those two primes(say $$$p_1$$$ and $$$p_2$$$)(all other valid numbers are already inserted in place), then, we would need to take $$$p_1$$$ and $$$2p_1$$$ at the left and $$$2p_2$$$ and $$$p_2$$$ at the right end of the construction. There is no room for the third! Damn thats beautiful.
I hope you guys liked the problemset. Editorial will be shared and prizes will be distributed soon.
When can we expect the editorial? Also, any plans of making solutions public? Ranklist is full of cheaters like 1_2_3_4_5_9 and arnabmanna.
Test cases and submissions of other participants are visible now. The editorial should be out in couple of days.
@arnabmanna got the 2nd position and @1_2_3_4_5_9 got the 3rd position solving the same problems. @1_2_3_4_5_9 is already known to be a cheater. I discovered @arnabmanna 's profile randomly from a comment on codeforces blog and was quite surprised to see his contest history. In the beginning he participate in 3 div2 round and 1 div3 round. He solved 1 problem in each div2 round and 2 problems in the div3 round. He was getting ranks around 8000-10000. And then in 1 week (from 29th Sept 2024 to 6th Oct 2024) he participated in one more div2 round solving 4 problems and getting a rank of 204. I don't like accusing people without evidence but I think this is good enough evidence to conclude he was cheating. In no world can someone who was solving 1 problem in div2 suddenly solve 4 problems in 1 week time and get a rank of 200 from 8000-10000.
Lol brother ..
You don't need to LOL anyone BROTHER — we can see your cheating history in your submission.
Initially, my roommate participated in the contest, and later, I decided to participate as well. While it might seem questionable, I want to clarify that my submission history tells the full story. If we analyze it, after the spike in my graph, there aren't any significant changes. Explaining this in detail would take more time, so I’m just providing an overview.
If bad English were a sport, you'd definitely be an expert. In Codeforces, you're just someone who reached Expert by cheating.
I'm interesting in why there's constraint on the sum of n in problem D, it's not needed in my solution.
Regardless of the constraint, the problem still required you to figure out the observation needed. We felt there was no point in increasing the implementation difficulty only slightly by removing the constraint.
How did you solve problem D ???
First, we can see primes that is greater than $$$n/2$$$ can never exist in $$$a$$$, because there's no number $$$x$$$ that $$$gcd$$$($$$x$$$,$$$p$$$) is not equal to $$$1$$$($$$p$$$ stands for the primes that is greater than $$$n/2$$$).
Then, for all primes greater than $$$n/3$$$ but not greater than $$$n/2$$$, there is only one number that can be its adjacent elements, so only two of them are valid.
Last, $$$1$$$ can never exist in $$$a$$$, because it has no divisor other than $$$1$$$.
After the above observation, we can store all primes not greater than $$$10^6$$$ in a vector, and for each test case, we can binary search for the number of the primes the two ranges above, and exclude them from the answer.
editorial where? synthborne looking for D's official approach
Editorial will be posted in a few days. We are sorry for the delay.
The editorial has been posted now. Thanks for participating!