HDU 5726 GCD
GCD
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Problem Description
Give you a sequence of $N(N≤100,000)$ integers : $a_1,\cdots,a_n(0<a_i≤1000,000,000)$. There are $Q(Q≤100,000)$ queries. For each query $l,r$ you have to calculate $\text{gcd}(a_l,,a_{l+1},\cdots,a_r)$ and count the number of pairs$(l′,r′)(1≤l<r≤N)$such that $\text{gcd}(a_{l′},a_{l′+1},\cdots,a_{r′})$ equal $\text{gcd}(a_l,a_{l+1},...,a_{r})$.
Input
The first line of input contains a number $T$, which stands for the number of test cases you need to solve.
The first line of each case contains a number $N$, denoting the number of integers.
The second line contains $N$ integers, $a_1,\cdots,a_n(0<a_i≤1000,000,000)$.
The third line contains a number $Q$, denoting the number of queries.
For the next $Q$ lines, $i\text{-th}$ line contains two number , stand for the $l_i,r_i$, stand for the $i\text{-th}$ queries.
Output
For each case, you need to output “Case #:t” at the beginning.(with quotes, t means the number of the test case, begin from 1).
For each query, you need to output the two numbers in a line. The first number stands for $\text{gcd}(a_l,a_{l+1}, \cdots,a_r)$ and the second number stands for the number of pairs$(l′,r′)$ such that $\text{gcd}(a_{l′},a_{l′+1},\cdots,a_{r′})$ equal $\text{gcd}(a_l,a_{l+1},\cdots,a_r)$.
Sample Input
1
5
1 2 4 6 7
4
1 5
2 4
3 4
4 4
Sample Output
Case #1:
1 8
2 4
2 4
6 1
Author
HIT
Source
2016 Multi-University Training Contest 1
题意:
支持查询: (1) 区间gcd, (2) gcd值等于k的区间数
Solution:
区间gcd的查询线段树即可解决, 另外还能支持单点修改. 但这题要求支持查询gcd值等于k的区间个数, 线段树就有点乏力了, 因为这个信息大概不太好通过合并区间信息来得到. 我们来考虑区间gcd的性质:
令$\gcd_r(l)\quad (1\le l \le r) $表示, $l$到$r$的$\gcd$. 不难看出:
- $\gcd_r(l)$随着$l$的增大是单调不减的
- $\gcd_r(l)$最多取$\log{a_r}$个值, 因为在区间左端点从$r$移动到$l$的过程中gcd每缩小到一个新值都是因为除以了上个gcd的某个因子, 因而至少缩小为上个gcd的$\frac{1}{2}$, 从而不同的区间$\gcd$值最多有$\log{a_r}$个
因此, 我们可以对每个右端点$r$, 维护函数$\gcd_r(l)$. 实现方法是:
用vector<pair<int,int>> f 存某个函数$\gcd_r(l)$的每一段 (最多有$\log{a_r}$段), f[i].first表示第$i$段的左端点, f[i].second表示第$i$段的函数值.
在维护这$n$个函数的过程中, 用map记录每个$\gcd$出现的次数 (不同$\gcd$值最多有$O(n\log{N})$个, 实际上远达不到这个值.
接下来我们考虑如何利用上面维护好的函数查询某个区间$[l,r]$的$\gcd$.
我们可以在函数$g_r(l)$中二分查询小于等于的$l$的first的最大值对应的second的值, 这便是答案.
言不尽意, 详见代码.
Implementation:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N(1e5+5); 5 typedef pair<int,int> P; 6 7 vector<P> f[N]; 8 unordered_map<int,long long> cnt; 9 int T, n, q, cs; 10 11 int main(){ 12 for(cin>>T; T--; ){ 13 cin>>n; 14 for(int i=1, gcd, pos; i<=n; i++){ 15 scanf("%d", &gcd), pos=i, f[i].clear(); 16 for(auto x:f[i-1]){ 17 if(__gcd(gcd, x.second)!=gcd) f[i].push_back({pos, gcd}); 18 gcd=__gcd(gcd, x.second), pos=x.first; 19 } 20 f[i].push_back({pos, gcd}); 21 } 22 cnt.clear(); 23 for(int i=1; i<=n; i++){ 24 int pos=i+1; 25 for(auto x:f[i]) 26 cnt[x.second]+=pos-x.first, pos=x.first; 27 } 28 cin>>q; 29 printf("Case #%d:\n", ++cs); 30 for(int l, r; q--; ){ 31 scanf("%d%d", &l, &r); 32 int gcd=lower_bound(f[r].begin(), f[r].end(), P(l, INT_MAX), greater<P>())->second; 33 printf("%d %lld\n", gcd, cnt[gcd]); 34 } 35 } 36 }