题解 CF475D 【CGCDSSQ】
今天学习了分治之后,做ppt中间的一道题,没错,就是这道^_^
首先
我们先来看一下题,题目中让我们求的是一段区间的gcd为x的对数有多少。
我们这样考虑一下,当我们的区间的数的个数越多,我们的gcd一定不会比原来的gcd大。
所以,我们就可以的到一个单调不减gcd序列。
这样我们就可以预处理出所有的gcd,这样我们就可以直接的出答案。
因为一个数x,它的gcd的个数一定不会超过 logxlogxlogx ,所以我们就可以进行分治,统计个数,分为左右两部分,两部分分别进行暴力计算,,对于中间的部分,由于两者的答案互不影响,所以我们使用乘法原理,这样,我们就可以计算出跨mid的答案。
其实,这就是分治的思想的体现,
就像这张图一样
(看起来还是可以的吧)
将所求的的问题划分为两部分,最后在反回的时候,将答案进行一系列的操作,最后求得答案。
这就是分治在题中的思想运用。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<vector> 6 #include<map> 7 using namespace std; 8 template<typename type> 9 void scan(type &x){ 10 type f=1;x=0;char s=getchar(); 11 while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} 12 while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} 13 x*=f; 14 } 15 int gcd(int a,int b){return !b?a:gcd(b,a%b);} 16 #define ll long long 17 const int N=1e5+7; 18 ll a[N],n,m; 19 vector <pair<int,int> >v[2];//分别记录左右两部分的答案 20 map <int,ll>ans;//统计答案 21 void solve(int l,int r){ 22 if(l==r){ 23 ans[a[l]]++;//如果l==r显然 24 return ; 25 } 26 int mid=(l+r)>>1; 27 solve(l,mid);solve(mid+1,r); 28 v[1].clear();v[0].clear(); 29 int last=a[mid],now=a[mid],cnt=0;//统计左半部分 30 for(int i=mid;i>=l;i--){ 31 now=gcd(now,a[i]); 32 if(last!=now){//如果gcd有变化,进行更新 33 v[0].push_back(make_pair(last,cnt)); 34 cnt=1; 35 last=now; 36 }else cnt++;//统计个数 37 }v[0].push_back(make_pair(last,cnt)); 38 last=now=a[mid+1],cnt=0; 39 for(int i=mid+1;i<=r;i++){//同理,右半部分 40 now=gcd(now,a[i]); 41 if(now!=last){ 42 v[1].push_back(make_pair(last,cnt)); 43 cnt=1; 44 last=now; 45 }else cnt++; 46 }v[1].push_back(make_pair(last,cnt)); 47 for(int i=0;i<v[0].size();i++){ 48 for(int j=0;j<v[1].size();j++){//跨mid答案统计 49 ans[gcd(v[0][i].first,v[1][j].first)]+=1LL*v[0][i].second*v[1][j].second; 50 } 51 } 52 } 53 int main(){ 54 scan(n); 55 for(int i=1;i<=n;i++){ 56 scan(a[i]); 57 } 58 solve (1,n); 59 scan(m); 60 while(m--){ 61 int x; 62 scan(x); 63 printf("%lld\n",ans[x]); 64 } 65 }
这道题就讲到这里了,有什么问题希望大家可以提出。