原题链接:HDU-5869
题意为给定一组数和一组询问,每个询问用一个区间表示,要求输出区间内所有连续子序列的不同gcd值。
这道题和HDU-3333非常像。区别在于这里需要处理的是gcd。而gcd有一个性质,即相同起点的序列的gcd非严格递减。
对比HDU-3333,这道题需要使每个gcd对应序列的左起点尽量向右。
扫描到位置i的时候,可以新得到的gcd只与前面的i-1个数有关,但事实上能产生的新gcd是有限的,大概有( log a_i )个。所以用gcds[][]存起来,每次用gcd[i-1]更新gcd[i]即可。
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 #define MAXN 100010 6 #define MAXM 100010 7 8 int gcd(int a,int b) 9 { 10 if(b==0) return a; 11 return gcd(b,a%b); 12 } 13 int d[MAXN]; 14 int n,m; 15 vector<pair<int,int> > q[MAXM]; 16 vector<pair<int,int> > gcds[MAXN]; 17 int vis[1000010]; 18 int ans[MAXM]; 19 int tree[MAXN]; 20 int lowbit(int x) 21 { 22 return x&(-x); 23 } 24 void add(int k,int num) 25 { 26 while(k<=n) 27 { 28 tree[k]+=num; 29 k+=lowbit(k); 30 } 31 } 32 int sum(int k) 33 { 34 int ans=0; 35 while(k) 36 { 37 ans+=tree[k]; 38 k-=lowbit(k); 39 } 40 return ans; 41 } 42 int main() 43 { 44 #ifdef LOCAL 45 freopen("in.txt","r",stdin); 46 #endif 47 while(scanf("%d%d",&n,&m)!=EOF) 48 { 49 for(int i=0;i<=n;i++) gcds[i].clear(); 50 for(int i=1;i<=n;i++) q[i].clear(); 51 memset(vis,0,sizeof(vis)); 52 memset(tree,0,sizeof(tree)); 53 for(int i=1;i<=n;i++) scanf("%d",&d[i]); 54 for(int i=1;i<=m;i++) 55 { 56 int l,r; 57 scanf("%d%d",&l,&r); 58 q[r].push_back(make_pair(l,i)); 59 } 60 for(int i=1;i<=n;i++) 61 { 62 if(vis[d[i]]) add(vis[d[i]],-1); 63 add(i,1); 64 vis[d[i]]=i; 65 gcds[i].push_back(make_pair(d[i],i)); 66 for(unsigned int j=0;j<gcds[(i-1)].size();j++) 67 { 68 int tmp1=gcd(gcds[(i-1)][j].first,d[i]),tmp2=gcds[(i-1)][j].second; 69 if(tmp1==gcds[i][gcds[i].size()-1].first) continue; 70 if(vis[tmp1]) add(vis[tmp1],-1); 71 add(tmp2,1); 72 vis[tmp1]=tmp2; 73 gcds[i].push_back(make_pair(tmp1,tmp2)); 74 } 75 for(unsigned int j=0;j<q[i].size();j++) 76 { 77 int tmp=q[i][j].second; 78 ans[tmp]=sum(i)-sum(q[i][j].first-1); 79 } 80 } 81 for(int i=1;i<=m;i++) printf("%d ",ans[i]); 82 } 83 return 0; 84 }