长沙理工校赛I题题解-连续区间的最大公约数

题目来源https://www.nowcoder.com/acm/contest/96/I

解题前们需要先知道几个结论:

首先,gcd是有区单调性的: gcd(L,R)>=gcd(L,R+d)  ,因为每添加一个数,gcd只会变小或者不变。

其次,以L左端点的所有区间的【GCD的种类数】一般不超过15,最多不超过31个,因为gcd每次变小时会除掉当前gcd的一个或多个质因子,所以质因数的个数,决定这个gcd

最多能变小几次,而质因子最多的数就是2^31。

 

预处理:

在解决问题之前我们先做几个预处理,至于这些预处理有什么用,在解题时会说明。

  • :为了快速查询区间gcd我们需要,用ST表先预处理一下数组,使得我们查询任意区间gcd的复杂下降至只需一个gcd(),即O(log(x)).   因为算gcd要一个log的复杂所以总复杂度,近似为 nlog(n)^2
  • 对于每个L,二分计算区间内每种gcd的起始右端点,比如区间[8,4,4,2] 的区间,以下标1开始的区间gcd有 8,4,2这3种gcd,对应的右端点分别为[1,2,4]。因为计算gcd要一个log,二分一个log,而gcd种类数是期望好像是只比O(n)稍大(我瞎猜,不会证【1】)。所以总复杂度是接近nlog(n)^2

计算:设gcd[L,R]=g,要计算有多少个子区间为,其实就算算对于每个Li,对应Ri至少要多少才能使得gcd[Li,Ri]=g。 而答案为Σ(R+1-Ri)

如果你对每个LI都计算R的话,再快也是O(区间长度的) ,没前途。这有个更优美的求法,其实对于任意的Li和g,对应的Ri我们都在预处理的时候算好了,

但是按Li查询的复杂度太高了,那么为啥不考虑一下按g查询

所以在预处理阶段,我们按g分组,并在每个组内并按Li排序(其实按Ri还是按Li都是一样的,因为组内有类似尺取的区间单调性,这点自己手动模拟一下就知道了),

并预处理Ri的前缀和。  接着查询L,R时,在g的组内二分(如果数据是随机的话,暴力也是行的)一下,L<=Li&&Ri<=R 的 区间,利用RI的前缀和一减就可以得分Σ(R+1-Ri)

将映射g到组id,用hash,可O(1).  平均组长log(n).,最坏组长O(n)  .   查询复杂度是 平均时log(log(n))  最快log(n)

 

加上预处理,最坏复杂度近似为 O(qlogn+nlog(n)^2)

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<algorithm>
  4 #include<math.h>
  5 #include<map>
  6 using namespace std;
  7 typedef long long ll;
  8 #define N 280000
  9 int dp[200005][18];
 10 int len[200005];
 11 int a[200005];
 12 struct node
 13 {
 14     int l,r;
 15     long long sum;
 16 } p;
 17 vector<node>e[1000005];
 18 map<int,int>mp;
 19 inline int cal(int l,int r)
 20 {
 21     if(l>r)
 22         return -1;
 23     int k=len[r-l+1];
 24     return __gcd(dp[l][k],dp[r-(1<<k)+1][k]);
 25 }
 26 inline  int findr(int l,int r,int g)
 27 {
 28     int k=len[r-l+1],b=l,i;
 29     for(i=1<<k; i; i>>=1)
 30     {
 31         if(b+i<=r&&cal(l,b+i)>=g)
 32         {
 33             b+=i;
 34         }
 35     }
 36     return b;
 37 }
 38 inline  int findl(int l,int r,int g)
 39 {
 40     int k=len[r-l+1],b=r,i;
 41     for(i=1<<k; i; i>>=1)
 42     {
 43         if(b-i>=l&&cal(l,b-i)<=g)
 44         {
 45             b-=i;
 46         }
 47     }
 48     return b;
 49 }
 50 int fl(int g,int x)
 51 {
 52     int l=0,r,m,k;
 53     k=mp[g];
 54     r=e[k].size();
 55     while(l+1<r)
 56     {
 57         m=(l+r)/2;
 58         if(e[k][m].l<x)
 59         {
 60             l=m;
 61         }
 62         else
 63         {
 64             r=m;
 65         }
 66     }
 67     return l;
 68 }
 69 int fr(int g,int x)
 70 {
 71     int l=0,r,m,k;
 72     k=mp[g];
 73     r=e[k].size();
 74     while(l+1<r)
 75     {
 76         m=(l+r)/2;
 77         if(e[k][m].r<=x)
 78         {
 79             l=m;
 80         }
 81         else
 82         {
 83             r=m;
 84         }
 85     }
 86     return l;
 87 }
 88 long long fun(int l,int r,int g)
 89 {
 90     long long ans=0;
 91     long long a,b;
 92     a=fl(g,l);;
 93     b=fr(g,r);
 94     int k=mp[g];
 95     ans=(b-a)*(r+1)-(e[k][b].sum-e[k][a].sum);
 96     return ans;
 97 }
 98 int main()
 99 {
100     int n,m,k,l,r,i,j,g,q,t,cas=1;
101     len[1]=0;
102     for(i=2; i<=100005; i++)
103     {
104         len[i]=len[i/2]+1;
105     }
106     scanf("%d",&t);
107     while(t--)
108     {
109         scanf("%d",&n);
110         memset(dp,0,sizeof(dp));
111         mp.clear();
112         for(i=1; i<=n; i++)
113         {
114             scanf("%d",&a[i]);
115             dp[i][0]=a[i];
116         }
117         for(j=1; j<=len[n]; j++)
118         {
119             for(i=1; i<=n; i++)
120             {
121                 dp[i][j]=__gcd(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
122             }
123         }
124         int ll;
125         for(i=1; i<=n; i++)
126         {
127             g=a[i];
128             ll=i;
129             while(1)
130             {
131                 if(mp.count(g)==0)
132                 {
133                     mp[g]=mp.size();
134                     p.l=0;
135                     p.r=0;
136                     p.sum=0;
137                     e[mp[g]].push_back(p);
138                 }
139                 p.l=i;
140                 ll=findr(i,n,g);
141                 p.r=findl(i,n,g);
142                 e[mp[g]].push_back(p);
143                 if(ll>=n)
144                     break;
145                 g=__gcd(g,a[ll+1]);
146             }
147         }
148         for(i=0; i<=mp.size(); i++)
149         {
150             for(j=1; j<e[i].size(); j++)
151             {
152                 e[i][j].sum=e[i][j-1].sum+e[i][j].r;
153             }
154 
155         }
156         scanf("%d",&q);
157         printf("Case #%d:\n",cas++);
158         while(q--)
159         {
160             scanf("%d%d",&l,&r);
161             g=cal(l,r);
162             printf("%d %lld\n",g,fun(l,r,g));
163         }
164 
165         for(i=0; i<mp.size(); i++)
166         {
167             e[i].clear();
168         }
169     }
170     return 0;
171 }
AC代码

 

posted @ 2018-04-24 22:15  强势围观  阅读(606)  评论(0编辑  收藏  举报