HDU 5726 GCD

题目:GCD

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5726

题意:给一个数组a,大小为n,接下来有m个询问,每次询问给出l、r,定义f[l,r]=gcd(al,al+1,...,ar),问f[l,r]的值 和 有多少对(l',r')使得f[l',r']=f[l,r]。n<=10万,m<=10万,1<=l<=r<=n,1<=l'<=r'。

思路:

  第一步比较简单,预处理一下,定义f[i][j]为:ai开始,连续2^j个数的最大公约数,所以f[1][0]=a[1],f[1][1]=gcd(a1,a2),f[1][2]=gcd(a1,a2,a3,a4)。其实就是动态规划,让i从1-n,让j从0-17,递推上去即可。

  递推公式如下:

  1. f[i][0]=a[i];

  2. f[i][j]=gcd(f[i][j-1],f[i+(1<<j-1)][j-1]);

  就如同f[1][2]=gcd(f[1][1],f[3][1])=gcd(gcd(f[1][0],f[2][0]),gcd(f[3][0],f[4][0]));

  通过上述预处理,查询时就只需O(logn)时间,如下:

  令k=log2(r-l+1),look(l,r)=gcd(f[l][k],f[r-(1<<k)+1][k]);

  注:f[l][k] 和 f[r-(1<<k)+1][k]可能会有重叠,但不影响最终的gcd值。

  比赛时第二步没想出来,太可惜了。。。

  第二步,我们可以枚举左端点 i 从1-n,对每个i,二分右端点,计算每种gcd值的数量,因为如果左端点固定,gcd值随着右端点的往右,呈现单调不增,而且gcd值每次变化,至少除以2,所以gcd的数量为nlog2(n)种,可以开map<int,long long>存每种gcd值的数量,注意n大小为10万,所以数量有可能爆int。

 

 1 #include<stdio.h>
 2 #include<math.h>
 3 #include<map>
 4 using namespace std;
 5 int f[100010][18];
 6 int a[100010];
 7 int n,m;
 8 int gcd(int a,int b)
 9 {
10   return b?gcd(b,a%b):a;
11 }
12 void rmq()
13 {
14   for(int j=1;j<=n;j++) f[j][0]=a[j];
15   for(int i=1;i<18;i++)
16   {
17     for(int j=1;j<=n;j++)
18     {
19       if(j+(1<<i)-1 <= n)
20       {
21         f[j][i]=gcd(f[j][i-1],f[j+(1<<i-1)][i-1]);
22       }
23     }
24   }
25 }
26 int look(int l,int r)
27 {
28   int k=(int)log2((double)(r-l+1));
29   return gcd(f[l][k],f[r-(1<<k)+1][k]);
30 }
31 map<int,long long> mp;
32 void setTable()
33 {
34   mp.clear();
35   for(int i=1;i<=n;i++)
36   {
37     int g=f[i][0],j=i;
38     while(j<=n)
39     {
40       int l=j,r=n;
41       while(l<r)
42       {
43         int mid=(l+r+1)>>1;
44         if(look(i,mid)==g) l=mid;
45         else r=mid-1;
46       }
47       mp[g]+=l-j+1;
48       j=l+1;
49       g=look(i,j);
50     }
51   }
52 }
53 int main()
54 {
55   int t,l,r;
56   int cas=1;
57   scanf("%d",&t);
58   while(t--)
59   {
60     printf("Case #%d:\n",cas++);
61     scanf("%d",&n);
62     for(int i=1;i<=n;i++)
63     {
64       scanf("%d",&a[i]);
65     }
66     rmq();
67     setTable();
68     scanf("%d",&m);
69     for(int i=0;i<m;i++)
70     {
71       scanf("%d%d",&l,&r);
72       int g=look(l,r);
73       printf("%d %I64d\n",g,mp[g]);
74     }
75   }
76   return 0;
77 }

 

 

 

 

posted @ 2016-07-20 10:25  hchlqlz  阅读(2871)  评论(4编辑  收藏  举报