还是想不到,真的觉得难,思路太巧妙
题意:给你一串数和一些区间,对于每个区间求出区间内每段连续值的不同gcd个数(该区间任一点可做起点,此点及之后的点都可做终点)
首先我们可以知道每次添加一个值时gcd要么不变要么减小,并且减小的幅度很大,就是说固定右端点时最多只能有(log2 a)个不同的gcd,而且我们知道gcd(gcd(a,b),c)=gcd(a,gcd(b,c)),所以我们可以使用n*(log2 n)的时间预处理出每个固定右端点的不同gcd的值和位置。解法就是从左到右,每次只需要使用上一次的不同gcd的值和位置。
离线处理每个询问,按照区间右端点从小到大排序,把预处理的每个gcd依次加入树状数组中,这样到一个询问的右端点时就区间查询不同gcd的个数(只需要使用左端点的位置)。树状数组在每种gcd最右一个左端点的位置(贪心想法)存这儿gcd的个数,但可能这个gcd之前出现过,因此我们可以再开一个数组存每种gcd的位置(保证每种gcd在最右边的位置)
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<string> #include<cstdio> #include<cstring> #include<stdlib.h> #include<iostream> #include<algorithm> using namespace std; #define eps 1E-8 /*注意可能会有输出-0.000*/ #define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型 #define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化 #define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0 #define mul(a,b) (a<<b) #define dir(a,b) (a>>b) typedef long long ll; typedef unsigned long long ull; const int Inf=1<<28; const double Pi=acos(-1.0); const int Mod=1e9+7; const int Max=100010; struct node { int mpos,gcd; } dp[Max][50]; //预处理 struct nide { int lef,rig,mpos; } qes[Max]; int bit[Max],mpos[Max*10];//树状数组处理此位置元素个数 某个gcd的位置(这时的最靠右) int num[Max],ans[Max],n; void Swap(int &a,int &b) { a^=b; b^=a; a^=b; return; } int Gcd(int a,int b)// a、b 均为偶数, gcd(a,b) = 2 * gcd(a/2, b/2) { if(a<b) Swap(a,b); int c=1; //a为偶数,b为奇数, gcd(a,b) = gcd(a/2 , b) while(a-b) //a为奇数,b为偶数,gcd(a,b) = gcd(a, b/2) { if(a&1) //a、b均为奇数, gcd(a,b) = gcd((a-b)/2, b) { if(b&1) { if(a>b) a=(a-b)>>1; else b=(b-a)>>1; } else b>>=1; } else { if(b&1) a>>=1; else c<<=1,a>>=1,b>>=1; } } return c*a; } int cntt[Max]; void Init()//预处理固定右端点时左边的g不通gcd { int tmp; cntt[0]=0; for(int i=1; i<=n; ++i) //固定右端点i { cntt[i]=1; dp[i][1].gcd=num[i]; dp[i][1].mpos=i; for(int j=1; j<=cntt[i-1]; ++j) { tmp=Gcd(dp[i][cntt[i]].gcd,dp[i-1][j].gcd); if(tmp!=dp[i][cntt[i]].gcd)//与每个右端点形成不同的gcd的位置要尽量靠右 { dp[i][++cntt[i]].gcd=tmp; dp[i][cntt[i]].mpos=dp[i-1][j].mpos; } } } return; } bool cmp(struct nide p1,struct nide p2)//排序右端点才可以统计 { return p1.rig<p2.rig; } int lowbit(int x) { return x&(-x); } void Add(int x,int y) { while(x<=n) { bit[x]+=y; x+=lowbit(x); } return; } int Sum(int x) { int sum=0; while(x) { sum+=bit[x]; x-=lowbit(x); } return sum; } void Solve(int q) { memset(bit,0,sizeof(bit)); memset(mpos,0,sizeof(mpos)); int k=1,sum=0; for(int i=1; i<=n; ++i) //枚举右端点 { for(int j=1; j<=cntt[i]; ++j) { if(!mpos[dp[i][j].gcd]) { sum++; Add(dp[i][j].mpos,1); mpos[dp[i][j].gcd]=dp[i][j].mpos; } else if(mpos[dp[i][j].gcd]<dp[i][j].mpos)//保证最右位置 { Add(mpos[dp[i][j].gcd],-1); Add(dp[i][j].mpos,1); mpos[dp[i][j].gcd]=dp[i][j].mpos; } } while(k<=q&&qes[k].rig==i) { ans[qes[k].mpos]=sum-Sum(qes[k].lef-1);//计算[lef,rig]的个数 ++k; } } return; } int main() { int q; while(~scanf("%d %d",&n,&q)) { for(int i=1; i<=n; ++i) scanf("%d",&num[i]); Init(); for(int i=1; i<=q; ++i) { scanf("%d %d",&qes[i].lef,&qes[i].rig); qes[i].mpos=i; } sort(qes+1,qes+1+q,cmp); Solve(q); for(int i=1; i<=q; ++i) printf("%d ",ans[i]); } return 0; }