• 长沙理工校赛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:
    ",cas++);
    158         while(q--)
    159         {
    160             scanf("%d%d",&l,&r);
    161             g=cal(l,r);
    162             printf("%d %lld
    ",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代码
  • 相关阅读:
    排序
    wine-qq 安装
    逆元模板
    最长上升子序列
    SGU[115] Calendar
    SGU[123] The sum
    SGU[105] Div 3
    SGU[102] Coprimes
    SGU[100] A+B
    poj-1325-Machine Schedule
  • 原文地址:https://www.cnblogs.com/qswg/p/8833970.html
Copyright © 2020-2023  润新知