GCD(st表+二分)

GCD

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 3432    Accepted Submission(s): 1227


Problem Description

Give you a sequence of N(N100,000) integers : a1,...,an(0<ai1000,000,000). There are Q(Q100,000) queries. For each query l,r you have to calculate gcd(al,,al+1,...,ar) and count the number of pairs(l,r)(1l<rN)such that gcd(al,al+1,...,ar) equal gcd(al,al+1,...,ar).
 

 

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, a1,...,an(0<ai1000,000,000).

The third line contains a number Q, denoting the number of queries.

For the next Q lines, i-th line contains two number , stand for the li,ri, stand for the i-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 gcd(al,al+1,...,ar) and the second number stands for the number of pairs(l,r) such that gcd(al,al+1,...,ar) equal gcd(al,al+1,...,ar).
 

 

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
 
 
题意:第一行T代表测试组数,第二行一个整数 N 代表长为 N 的序列,再一行是一个整数 Q ,代表有 Q 次询问,
然后 Q 行,每行两个整数 l,r 输出区间[l,r]的gcd 和有多少个区间 gcd 等于 gcd[l,r]
 
题解:想了很久,没想到好办法,后来发现RMQ这种问题,线段树还是不行的,如果离线的话,st表查询时间复杂度比线段树更小,为O(1)
st算法是用来求解给定区间RMQ的最值
学了st表后,查询有多少区间gcd==gcd[l,r]还是不知道怎么搞,题解是,用二分离线预处理...
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<map>
 6 using namespace std;
 7 
 8 #define LL long long
 9 #define MAXN 100005
10 
11 int n;
12 int num[MAXN];
13 int mn[MAXN];
14 int dp[MAXN][20];
15 map<int,LL> res;
16 
17 int gcd(int a,int b)
18 {   return b==0?a:gcd(b,a%b);}
19 
20 void Init()
21 {
22     mn[0]=-1;
23     for (int i=1;i<=n;i++)
24     {
25         mn[i]=((i&(i-1))==0)?mn[i-1]+1:mn[i-1];
26         dp[i][0]=num[i];
27     }
28     for (int j=1;j<=mn[n];j++)
29         for (int i=1;i+(1<<j)-1<=n;i++)
30         {
31             dp[i][j]=gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
32         }
33 }
34 
35 int st_calc(int l,int r)
36 {
37     int k = mn[r-l+1];
38     return gcd(dp[l][k],dp[r-(1<<k)+1][k]);
39 }
40 
41 void erfen()
42 {
43     res.clear();
44     for (int i=1;i<=n;i++)  //以i为左起点的区间的所有公约数
45     {
46         int cur = i , gc = num[i];  //用二分求出
47         while (cur <= n)
48         {
49             int l=cur,r=n;
50             while(l<r)
51             {
52                 int mid = (l+r+1)>>1;
53                 if (st_calc(i,mid)==gc) l=mid;
54                 else r = mid -1;
55             }
56             if (res.count(gc)) res[gc]+=l-cur+1;
57             else res[gc]=l-cur+1;
58             cur = l + 1;
59             if (cur<=n)
60                 gc = gcd(gc,num[cur]);
61         }
62     }
63 }
64 
65 int main()
66 {
67     int t;
68     int cas=0;
69     scanf("%d",&t);
70     while (t--)
71     {
72         scanf("%d",&n);
73         for (int i=1;i<=n;i++)
74             scanf("%d",&num[i]);
75         Init();
76 
77         erfen();
78 
79         int q;
80         scanf("%d",&q);
81         printf("Case #%d:\n",++cas);
82         while (q--)
83         {
84             int x,y;
85             scanf("%d%d",&x,&y);
86             int kk = st_calc(x,y);
87             printf("%d %lld\n",kk,res[kk]);
88         }
89     }
90     return 0;
91 }
View Code

 

 
 
posted @ 2017-04-05 22:05  happy_codes  阅读(535)  评论(0编辑  收藏  举报