cppAcademy
FROM LEARNING TO COMPETING

Real problems.
Your next challenge.

Explore the Group C Olympiad and Autumn Tournament archive.
Choose a topic. Find a challenge. Build your solution.

274 tasks: 213 from the Municipal, Regional, and National Olympiad rounds (2009–2026), 57 Autumn Tournament tasks (2007–2025), and 4 previously featured problems. 207 tasks have independently written English summaries, analyses, and C++17 solutions; the remaining entries link to Arena. Difficulty labels are learning estimates, not official ratings. Special formats and source clarifications are explained on affected tasks.
GROUP C2022 · HardDivisor sieves and binary search

Range Queries for Divisors or Multiples

Autumn Tournament (2022)

Submission name: divide.cpp

Problem summary

An array contains distinct positive integers. Each query L R d asks how many positions in the inclusive range have a value dividing d or divisible by d. A value equal to d must be counted only once.

Input

N Q, N distinct values, then Q triples L R d with 1-based positions.

Output

Q answers on one line, in query order.

Constraints

1 ≤ N,Q ≤100000; 1 ≤ a[i],d ≤200000; all array values are distinct; 1 ≤ L ≤ R ≤ N.

Worked example

Original statement sample.

Input

8 5
12 10 3 18 6 72 28 42
1 8 6
3 7 7
2 6 9
1 5 5
4 8 4

Output

6 1 3 1 2

Solution analysis

  1. Store position[value], or 0 if that value is absent. For every potential query value d, build a list of array positions satisfying one of the two divisibility relations. The distinct-value guarantee makes one position per number sufficient.
  2. Enumerate every pair divisor,multiple by a sieve loop: multiple=divisor,2·divisor,... . If the multiple occurs in the array, add its position to the divisor’s query list. If the divisor occurs, add its position to the multiple’s query list. On the equal pair perform only the first addition, preventing double counting.
  3. Sort every position list. A query answer is upper_bound(R)−lower_bound(L) in d’s list. In the sample query[1,8],d=6, the qualifying values are 12,3,18,6,72,42, yielding 6. For[4,8],d=4, only 72 and 28 qualify, yielding 2.

Why it works

The sieve enumerates exactly all ordered integer divisibility relationships. For a fixed d, every present multiple is added through the first rule and every present proper divisor through the second. These cases are disjoint except equality, which is deliberately included once. Sorting does not change membership; the two binary searches count exactly the retained positions in the inclusive interval.

Complexity: Let V=200000 and P be the number of stored position entries. Sieve enumeration takes O(V log V), sorting all lists takes O(P log N), and Q queries take O(Q log N). P=O(V log V), so memory is O(V+N+P). The harmonic-series bound comes from Σ floor(V/d).

Watch out. The array values are distinct; without that promise, position[value] would lose occurrences. Count equality only once. Both divisors and multiples matter: checking only a[i]%d==0 misses proper divisors of d.

Commented C++17 solution

An independently written complete program with standard headers.

#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    int queries;
    cin>>n>>queries;
    const int limit=200000;
    vector<int>position(limit+1,0);
    for(int index=1;index<=n;index++){
        int value;
        cin>>value;
        position[value]=index;
    }
    vector<vector<int>>matching(limit+1);
    for(int divisor=1;divisor<=limit;divisor++){
        for(int multiple=divisor;multiple<=limit;multiple+=divisor){
            if(position[multiple]!=0){
                matching[divisor].push_back(position[multiple]);
            }
            // Equality already belongs to the first case.
            if(multiple!=divisor&&position[divisor]!=0){
                matching[multiple].push_back(position[divisor]);
            }
        }
    }
    for(auto&positions:matching){
        sort(positions.begin(),positions.end());
    }
    for(int query=0;query<queries;query++){
        int left;
        int right;
        int value;
        cin>>left>>right>>value;
        const auto&positions=matching[value];
        int answer=static_cast<int>(upper_bound(positions.begin(),positions.end(),right)-
            lower_bound(positions.begin(),positions.end(),left));
        if(query>0){
            cout<<' ';
        }
        cout<<answer;
    }
    cout<<'\n';
}

Source: Arena Olimpiici ↗