C. Candy Store
C. Candy Store
The store sells $n$ types of candies with numbers from $1$ to $n$. One candy of type $i$ costs $b_i$ coins. In total, there are $a_i$ candies of type $i$ in the store.
You need to pack all available candies in packs, each pack should contain only one type of candies. Formally, for each type of candy $i$ you need to choose the integer $d_i$, denoting the number of type $i$ candies in one pack, so that $a_i$ is divided without remainder by $d_i$.
Then the cost of one pack of candies of type $i$ will be equal to $b_i \cdot d_i$. Let's denote this cost by $c_i$, that is, $c_i = b_i \cdot d_i$.
After packaging, packs will be placed on the shelf. Consider the cost of the packs placed on the shelf, in order $c_1, c_2, \ldots, c_n$. Price tags will be used to describe costs of the packs. One price tag can describe the cost of all packs from $l$ to $r$ inclusive if $c_l = c_{l+1} = \ldots = c_r$. Each of the packs from $1$ to $n$ must be described by at least one price tag. For example, if $c_1, \ldots, c_n = [4, 4, 2, 4, 4]$, to describe all the packs, a $3$ price tags will be enough, the first price tag describes the packs $1, 2$, the second: $3$, the third: $4, 5$.
You are given the integers $a_1, b_1, a_2, b_2, \ldots, a_n, b_n$. Your task is to choose integers $d_i$ so that $a_i$ is divisible by $d_i$ for all $i$, and the required number of price tags to describe the values of $c_1, c_2, \ldots, c_n$ is the minimum possible.
For a better understanding of the statement, look at the illustration of the first test case of the first test:
Let's repeat the meaning of the notation used in the problem:
$a_i$ — the number of candies of type $i$ available in the store.
$b_i$ — the cost of one candy of type $i$.
$d_i$ — the number of candies of type $i$ in one pack.
$c_i$ — the cost of one pack of candies of type $i$ is expressed by the formula $c_i = b_i \cdot d_i$.
Input
Each test contains multiple test cases. The first line contains the number of test cases $t$ ($1 \le t \le 100\,000$). Description of the test cases follows.
The first line of each test case contains a single integer $n$ ($2 \le n \le 200\,000$) — the number of types of candies.
Each of the next $n$ lines of each test case contains two integers $a_i$ and $b_i$ ($1 \le a_i \le 10^9$, $1 \le b_i \le 10\,000$) — the number of candies and the cost of one candy of type $i$, respectively.
It is guaranteed that the sum of $n$ over all test cases does not exceed $200\,000$.
Output
For each test case, output the minimum number of price tags required to describe the costs of all packs of candies in the store.
Example
input
5 4 20 3 6 2 14 5 20 7 3 444 5 2002 10 2020 2 5 7 7 6 5 15 2 10 3 7 7 5 10 1 11 5 5 1 2 2 8 2 6 7 12 12 3 5 3 9 12 9 3 1000000000 10000
output
2 1 3 2 5
Note
In the first test case, you can choose $d_1 = 4$, $d_2 = 6$, $d_3 = 7$, $d_4 = 5$. Then the cost of packs will be equal to $[12, 12, 35, 35]$. $2$ price tags are enough to describe them, the first price tag for $c_1, c_2$ and the second price tag for $c_3, c_4$. It can be shown that with any correct choice of $d_i$, at least $2$ of the price tag will be needed to describe all the packs. Also note that this example is illustrated by a picture in the statement.
In the second test case, with $d_1 = 4$, $d_2 = 2$, $d_3 = 10$, the costs of all packs will be equal to $20$. Thus, $1$ price tag is enough to describe all the packs. Note that $a_i$ is divisible by $d_i$ for all $i$, which is necessary condition.
In the third test case, it is not difficult to understand that one price tag can be used to describe $2$nd, $3$rd and $4$th packs. And additionally a price tag for pack $1$ and pack $5$. Total: $3$ price tags.
解题思路
先给出比赛时想到的做法。思路比较暴力,代码也很长。
对于总数量为$a_i$的糖果,第$i$种糖果的每一小包中允许的数量为$a_i$的约数。因此容易想到直接分解得到每个$a_i$的所有约数,然后每个约数都乘上$b_i$,那么就得到第$i$种糖果允许存在的所有代价。最后从前往后枚举所有糖果并尽可能取合法代价的交集,这部分当时比赛没想到,后面再细说。
首先每个$a_i$最大能够取到${10}^9$,如果直接暴力分解约数那么时间复杂度就是$O(\sqrt{{10}^9} \cdot n)$,肯定会TLE。这个时候我们就退而求次,先筛出$\sqrt{{10}^9}$内的所有素数,然后对$a_i$分解质因数,由于在${10}^9$内一个数最多有$1344$个约数,因此可以直接通过质因数来暴搜找到$a_i$的所有约数,那么时间复杂度就降到了$O \left( n \cdot \left( \frac{\sqrt{{10}^9}}{\ln{\sqrt{{10}^9}}} + 1344 \right) \right)$级别,其中$\sqrt{{10}^9}$内的素数个数为$3400$个左右,因此计算量大约是${10}^8$级别,时间限制为$3s$感觉不会T(?)。
其实按照我现在这种做法在cf上面不会T(2023-03-27),但为了保证严谨性,我特意构造了组极限数据跑了下,然后我成功把自己给hack了。构造的数据很简单,就是$a = 735134400, \ b = 1$和$a = 3, \ b = 1$共交替出现$2 \times {10}^5$次就可以了。其中$735134400$有$1344$个约数,且$3 \nmid 735134400$。
但还是继续说一下做法吧。现在给出了种还可以的分解质因数的方法,接下来就是如何选代价的问题了。假设第$i$种糖果的代价为集合$c_i$,如果第$i+1$种糖果想与第$i$种糖果的代价一样,那么很明显能选的代价为两个集合$c_i$与$c_{i+1}$的交集。以此类推,如果第$l$种糖果到第$r$种糖果的代价都相同,那么就要满足$\bigcap\limits_{i = l}^{r} c_i \ne \varnothing$。
因此我们可以从前往后枚举$i$,对于第$i$种糖果每次求$c_i$与$c_{i-1}$的交集(此时的$c_{i-1}$是$c_l$到$c_{i-1}$的交集),如果得到的结果不为空,那么说明第$l \sim i$种糖果可以有相同的代价。如果直接暴力求两个集合的交集那么时间复杂度是$O(|c_{i}| \cdot |c_{i-1}|)$,一共要求$n$个集合的交集,必定会超时。因此在求交集的部分需要优化。
注意到对于$\forall x \in c_{i} \cap c_{i-1}$必定满足$x \mid b_i$且$\tfrac{x}{b_i} \mid a_i$,因此我们只需要枚举$c_{i-1}$的每一个元素$x$,如果$x$满足这两个条件那么我们就保留$x$,时间复杂度就降到了$O(n \cdot |c_{i-1}|) \approx O(1344 \cdot n)$。可以发现甚至第$i$种糖果的代价都不需要求了(即不需要对$a_i$分解约数)。而如果在这个过程中没有选到任何元素,即交集为空,我们才需要求第$i$种糖果的代价$c_i$。
TLE代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 2e5 + 10; 7 8 int primes[N], cnt; 9 bool vis[N]; 10 int a[N], b[N]; 11 vector<vector<int>> fs; 12 vector<LL> c[N]; 13 14 void get_prime(int n) { 15 for (int i = 2; i <= n; i++) { 16 if (!vis[i]) primes[cnt++] = i; 17 for (int j = 0; primes[j] <= n / i; j++) { 18 vis[primes[j] * i] = true; 19 if (i % primes[j] == 0) break; 20 } 21 } 22 } 23 24 void dfs(int k, int u, int prod) { 25 if (u == fs.size()) { // 找到约数prod 26 c[k].push_back(1ll * prod * b[k]); // 记录第k种糖果所有可能的代价 27 return; 28 } 29 int t = 1, p = fs[u][0], s = fs[u][1]; 30 for (int i = 0; i <= s; i++) { 31 dfs(k, u + 1, prod * t); 32 t *= p; 33 } 34 } 35 36 void get(int k) { 37 fs.clear(); 38 int t = a[k]; 39 for (int i = 0; primes[i] <= t / primes[i]; i++) { // 质因数分解 40 int p = primes[i]; 41 if (t % p == 0) { 42 int s = 0; 43 while (t % p == 0) { 44 t /= p; 45 s++; 46 } 47 fs.push_back({p, s}); 48 } 49 } 50 if (t > 1) fs.push_back({t, 1}); 51 dfs(k, 0, 1); // 暴搜出a[k]的所有约数 52 } 53 54 void solve() { 55 int n; 56 scanf("%d", &n); 57 for (int i = 0; i < n; i++) { 58 scanf("%d %d", a + i, b + i); 59 c[i].clear(); 60 } 61 get(0); // 求第0种糖果的所有可能代价 62 int ret = 1; // 答案至少是1 63 for (int i = 1; i < n; i++) { 64 for (auto &x : c[i - 1]) { // 求交集 65 if (x % b[i] == 0 && a[i] % (x / b[i]) == 0) c[i].push_back(x); 66 } 67 if (c[i].empty()) { // 交集为空 68 ret++; // 需要的代价种类+1 69 get(i); // 求第i种糖果的所有可能代价 70 } 71 } 72 printf("%d\n", ret); 73 } 74 75 int main() { 76 get_prime(N - 1); // 筛出sqrt(1e9)内的质数,用于质因数分解 77 int t; 78 scanf("%d", &t); 79 while (t--) { 80 solve(); 81 } 82 83 return 0; 84 }
下面给出正解,是真想不到。
假设第$l$种糖果到第$r$种糖果的代价一样,令$c_i = d_i \cdot b_i$,即有$c_l = c_{l+1} = \cdots = c_r = x$,其中$d_i \mid a_i$。
那么可以发现对于$\forall i \in [l,r]$,$b_i$是$x$的一个因子,即$x$是$b_i$的倍数,因此必然有$\operatorname{lcm} \left[ b_l, \ldots, b_r \right] \mid x$。
同时由于$d_i \mid a_i$,因此有$d_i \cdot b_i \mid a_i \cdot b_i$,即$x$是$a_i \cdot b_i$的一个因子,这就等价于$x \mid \gcd(c_l, \ldots,c_{r})$。
因此如果有$c_l = c_{l+1} = \cdots = c_r$,那么必然有$\operatorname{lcm} \left[ b_l, \ldots, b_r \right] \mid \gcd(a_l \cdot b_l, \ldots, a_r \cdot b_r)$,反过来也成立。
然后从前往后枚举,维护连续序列的最大公约数$d$和最小公倍数$m$,贪心地选择,即如果满足$m \mid d$则使用同一个代价,而如果$m \nmid d$则说明需要开另外一个代价。
AC代码如下,时间复杂度为$O(n \cdot \log{{10}^9})$:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 LL gcd(LL a, LL b) { 7 return b ? gcd(b, a % b) : a; 8 } 9 10 LL lcm(LL a, LL b) { 11 return a / gcd(a, b) * b; 12 } 13 14 void solve() { 15 int n; 16 scanf("%d", &n); 17 LL d = 0, m = 1; 18 int ret = 1; 19 while (n--) { 20 LL a, b; 21 scanf("%lld %lld", &a, &b); 22 m = lcm(m, b); 23 d = gcd(d, a * b); 24 if (d % m) { 25 ret++; 26 d = a * b, m = b; 27 } 28 } 29 printf("%d\n", ret); 30 } 31 32 int main() { 33 int t; 34 scanf("%d", &t); 35 while (t--) { 36 solve(); 37 } 38 39 return 0; 40 }
参考资料
Codeforces Round 860 (Div. 2) A~E:https://zhuanlan.zhihu.com/p/617245703
Editorial of Codeforces Round 860 (Div. 2):https://codeforces.com/blog/entry/114208
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17263049.html