这题挺有意思哈!!!看别人写的博客,感觉瞬间就懂了。
这道题大概题意就是,给一串序列,我们要查找到l-r区间内,满足min(a[ i ],a[ j ]) = gcd(a[ i ],a[ j ])
其实我们把这个东西看成一个二元组,<i,j> 二元组满足 min(a[ i ],a[ j ]) = gcd(a[ i ],a[ j ])
为了保证唯一性,<i,j>二元组满足i<j ,min(a[ i ],a[ j ]) = gcd(a[ i ],a[ j ])
由于是排列,我们可以枚举所有的满足条件的<i,j>,并且保证二元组唯一。
这样我们就转换为一个二维偏序问题。
对于询问<l,r>我们需要回答满足条件的二元组<i,j> 满足 l<=i 且 j<=r 的二元组的组数。
那我们把询问全部离线并加入修改操作,然后排序第一维度的r,保证r的有序,对于r相同的修改和询问,先进行修改
(因为先要生成序列,询问实际上是后面进行的),维护了r后,保证了树状数组里面<i,j> j<=r,然后查询>=l的个数。
查询query(n)-query(l-1)即可,注意如果是l==1的话,避免树状数组超时直接查query(n)。就实现了二维偏序的查询。
#include<bits/stdc++.h> using namespace std; const int maxx = 1e6+6; struct node{ int l,r,id; bool operator < (const node &s) const { if (r==s.r){ return id<s.id; } return r<s.r; } }q[2*maxx]; int ans[maxx]; int pos[maxx]; int tot,n; int sum[maxx]; int lowbit(int x){ return x&(-x); } void add(int x,int w){ for(int i=x;i<=n;i+=lowbit(i)){ sum[i]+=w; } } int query(int x){ int s=0; for (int i=x;i;i-=lowbit(i)){ s+=sum[i]; } return s; } int main(){ int m; scanf("%d%d",&n,&m); tot=0; memset(ans,0,sizeof(ans)); memset(pos,0,sizeof(pos)); memset(sum,0,sizeof(sum)); int tmp; for (int i=1;i<=n;i++){ scanf("%d",&tmp); pos[tmp]=i; } for (int i=1;i<=n;i++){ for (int j=2;i*j<=n;j++){ q[++tot]=node{min(pos[i],pos[i*j]),max(pos[i],pos[i*j]),0}; } } int l,r; for (int i=1;i<=m;i++){ scanf("%d%d",&l,&r); q[++tot]=node{l,r,i}; } sort(q+1,q+1+tot); for (int i=1;i<=tot;i++){ if (q[i].id==0){ add(q[i].l,1); }else { if (q[i].l==1){ ans[q[i].id]=query(n); }else{ ans[q[i].id]=query(n)-query(q[i].l-1); } } } for (int i=1; i<=m; i++) { printf("%d ",ans[i]); } return 0; }