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+MlogM) time.
[/hide]
翰林课程体验,退费流程快速投诉邮箱: yuxi@linstitute.net 沪ICP备2023009024号-1