The cows are hard at work trying to invent interesting new games to play. One of their current endeavors involves a set of?NN?intervals (1≤N≤2?1051≤N≤2?105), where the?iith interval starts at position?aiai?on the number line and ends at position?bi≥aibi≥ai. Both?aiai?and?bibi?are integers in the range?0…M0…M, where?1≤M≤50001≤M≤5000.
To play the game, Bessie chooses some interval (say, the?iith interval) and her cousin Elsie chooses some interval (say, the?jjth interval, possibly the same as Bessie's interval). Given some value?kk, they win if?ai+aj≤k≤bi+bjai+aj≤k≤bi+bj.
For every value of?kk?in the range?0…2M0…2M, please count the number of ordered pairs?(i,j)(i,j)?for which Bessie and Elsie can win the game.
The first line of input contains?NN?and?MM. Each of the next?NN?lines describes an interval in terms of integers?aiai?and?bibi.
Please print?2M+12M+1?lines as output, one for each value of?kk?in the range?0…2M0…2M.
2 5 1 3 2 5
0 0 1 3 4 4 4 3 3 1 1
In this example, for just?k=3k=3, there are three ordered pairs that will allow Bessie and Elie to win:?(1,1)(1,1),?(1,2),(1,2),?and?(2,1)(2,1).
Note that output values might be too large to fit into a 32-bit integer, so you may want to use 64-bit integers (e.g., "long long" ints in C or C++).
Problem credits: Benjamin Qi
[/hide]
(Analysis by Benjamin Qi)
A direct implementation of the definition in the problem statement to compute the win counts for all?kk?involves a nested loop over all pairs of intervals and all?kk. This runs in?O(N2M)O(N2M)?time, and is only sufficient for the first two test cases.
#include <bits/stdc++.h> using namespace std; int main() { cin.tie(0)->sync_with_stdio(0); int N, M; cin >> N >> M; vector<pair<int, int>> ivals(N); for (auto &ival : ivals) cin >> ival.first >> ival.second; vector<int64_t> win_counts(2 * M + 1); for (auto [a_i, b_i] : ivals) for (auto [a_j, b_j] : ivals) for (int k = a_i + a_j; k <= b_i + b_j; ++k) ++win_counts.at(k); for (auto win : win_counts) cout << win << "\n"; }
Using?prefix sums, we can remove the loop over?kk, reducing the time complexity to?O(N2+M)O(N2+M). Ths idea is to add one to?win_countwin_count?just before an interval of wins begins and subtract one from?win_countwin_count?just after an interval of wins ends. This is sufficient for the first five test cases, which all have relatively small?NN.
#include <bits/stdc++.h> using namespace std; int main() { cin.tie(0)->sync_with_stdio(0); int N, M; cin >> N >> M; vector<pair<int, int>> ivals(N); for (auto &ival : ivals) cin >> ival.first >> ival.second; vector<int64_t> win_start(2 * M + 1), win_end(2 * M + 1); for (auto [a_i, b_i] : ivals) for (auto [a_j, b_j] : ivals) { ++win_start.at(a_i + a_j); ++win_end.at(b_i + b_j); } int64_t win_count = 0; for (int i = 0; i <= 2 * M; ++i) { win_count += win_start.at(i); cout << win_count << "\n"; win_count -= win_end.at(i); } }
For full credit, we take advantage of?MM?being relatively small. Let's by noting that?win_startwin_start?and?win_endwin_end?may be computed separately.
#include <bits/stdc++.h> using namespace std; int main() { cin.tie(0)->sync_with_stdio(0); int N, M; cin >> N >> M; vector<pair<int, int>> ivals(N); for (auto &ival : ivals) cin >> ival.first >> ival.second; vector<int64_t> win_start(2 * M + 1), win_end(2 * M + 1); for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) ++win_start.at(ivals.at(i).first+ivals.at(j).first); for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) ++win_end.at(ivals.at(i).second+ivals.at(j).second); int64_t win_count = 0; for (int i = 0; i <= 2 * M; ++i) { win_count += win_start.at(i); cout << win_count << "\n"; win_count -= win_end.at(i); } }
But the first nested loop is doing a lot of repeated work because many intervals will share the same left endpoints (similar reasoning holds for the right endpoints). If we instead iterate over all?distinct?left endpoints then we may reduce the runtime of each nested for loop to?O(M2)O(M2). We can do this by maintaining a length-M+1M+1?array?a_freq[a]a_freq[a]?that keeps track of the number of occurrences of?aa?over all intervals, and similarly for?bb. The overall time complexity is?O(N+M2)O(N+M2).
#include <bits/stdc++.h> using namespace std; int main() { cin.tie(0)->sync_with_stdio(0); int N, M; cin >> N >> M; vector<pair<int, int>> ivals(N); for (auto &ival : ivals) cin >> ival.first >> ival.second; vector<int64_t> win_start(2 * M + 1), win_end(2 * M + 1); { vector<int64_t> a_freq(M + 1); for (int i = 0; i < N; ++i) ++a_freq.at(ivals.at(i).first); for (int i = 0; i <= M; ++i) for (int j = 0; j <= M; ++j) win_start.at(i + j) += a_freq.at(i) * a_freq.at(j); } { vector<int64_t> b_freq(M + 1); for (int i = 0; i < N; ++i) ++b_freq.at(ivals.at(i).second); for (int i = 0; i <= M; ++i) for (int j = 0; j <= M; ++j) win_end.at(i + j) += b_freq.at(i) * b_freq.at(j); } int64_t win_count = 0; for (int i = 0; i <= 2 * M; ++i) { win_count += win_start.at(i); cout << win_count << "\n"; win_count -= win_end.at(i); } }
Danny Mittal's (similar) code:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringTokenizer; public class IntervalConvolution { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer tokenizer = new StringTokenizer(in.readLine()); int n = Integer.parseInt(tokenizer.nextToken()); int m = Integer.parseInt(tokenizer.nextToken()); long totalPairs = ((long) n) * ((long) n); long[] aFreq = new long[m + 1]; long[] bFreq = new long[m + 1]; for (int j = 1; j <= n; j++) { tokenizer = new StringTokenizer(in.readLine()); int a = Integer.parseInt(tokenizer.nextToken()); int b = Integer.parseInt(tokenizer.nextToken()); aFreq[a]++; bFreq[b]++; } long[] aSumFreq = new long[(2 * m) + 1]; long[] bSumFreq = new long[(2 * m) + 1]; for (int x = 0; x <= m; x++) { for (int y = 0; y <= m; y++) { aSumFreq[x + y] += aFreq[x] * aFreq[y]; bSumFreq[x + y] += bFreq[x] * bFreq[y]; } } long aValid = aSumFreq[0]; long bValid = totalPairs; StringBuilder out = new StringBuilder(); for (int x = 0; x <= 2 * m; x++) { if (x > 0) { aValid += aSumFreq[x]; bValid -= bSumFreq[x - 1]; } long res = aValid + bValid - totalPairs; out.append(res).append('\n'); } System.out.print(out); } }
Interestingly, computing?win_startwin_start?from?a_freqa_freq?can be thought of as squaring the degree?MM-polynomial represented by?a_freqa_freq?(and polynomial multiplication is also known as?convolution). Using?fast polynomial multiplication?would allow us to solve this problem in?O(N+MlogM)O(N+Mlog?M)?time.
[/hide]
? 2025. All Rights Reserved. 沪ICP备2023009024号-1