给n个数,m个询问, 问任意区间内与其它数互质的数有多少个
比如3个数1 2 4,询问[1,3] 那么答案是1
千万要记住,这样的题目,如果你不转变下,使劲往线段树想(虽然转变之后,也说要用到线段树,但是维护的东西不同了),那么会发现这样的题目,区间与区间之间是无法传递信息的,
区间与区间是无法传递信息的,区间与区间之间是无法传递信息的,重要的东西说三遍。
设n个数,存在数组a[]里面
我们预处理出,L[],和R[],L[i] 表示从i往左,第一个与a[i]不互质的数的位置+1, R[i]表示从i往右,第一个与a[i]不互质的数的位置-1
即L[i] 表示 [L[i],i]内的所有数都与a[i]互质,R[i]表示[i,R[i]]内的所有数都与a[i]互质
然后我们离线处理,将所有的询问按照左端点排序
然后枚举左端点i,将所有L[j] = i的 [j,R[j]]区间+1,因为当左端点为i时,L[j]=i的数都在各自的有效区间[j,R[j]]里面生效了
当i=询问的区间的左端点时,只要查询右端点被加了多少次就行了。
走过i时,第i个数不再生效,所以将[i,R[i]]区间-1
1 #include<cstdio> 2 #include<iostream> 3 #include<string.h> 4 #include<algorithm> 5 #include <vector> 6 using namespace std; 7 const int N = 200000 + 10; 8 vector<int> prime[N]; 9 vector<int> cL[N]; 10 int a[N],L[N],R[N]; 11 int mark[N]; 12 int tree[N<<2],lazy[N<<2]; 13 int ans[N]; 14 void pushDown(int rt) 15 { 16 if(lazy[rt]) 17 { 18 lazy[rt<<1] += lazy[rt]; 19 lazy[rt<<1|1] += lazy[rt]; 20 tree[rt<<1] += lazy[rt]; 21 tree[rt<<1|1] += lazy[rt]; 22 lazy[rt] = 0; 23 } 24 } 25 void update(int l, int r, int rt, int L, int R, int val) 26 { 27 if(L<=l && R>=r) 28 { 29 lazy[rt]+=val; 30 tree[rt] += val; 31 return; 32 } 33 pushDown(rt); 34 int mid = (l+r)>>1; 35 if(L<=mid) 36 update(l,mid,rt<<1,L,R,val); 37 if(R>mid) 38 update(mid+1,r,rt<<1|1,L,R,val); 39 40 } 41 int query(int l, int r, int rt, int pos) 42 { 43 if(l==r) 44 { 45 return tree[rt]; 46 } 47 pushDown(rt); 48 int mid = (l+r)>>1; 49 if(pos<=mid) 50 return query(l,mid,rt<<1,pos); 51 else 52 return query(mid+1,r,rt<<1|1,pos); 53 } 54 struct Node 55 { 56 int l,r,id; 57 bool operator<(const Node&rhs)const 58 { 59 return l < rhs.l; 60 } 61 }q[N]; 62 63 void getPrime() 64 { 65 for(int i=2;i<=200000;++i) 66 { 67 if(!mark[i]) 68 for(int j=i;j<=200000;j+=i) 69 { 70 mark[j] = true; 71 prime[j].push_back(i);//得到j的所有素数因子i 72 } 73 } 74 } 75 void init(int n) 76 { 77 memset(mark,0,sizeof(mark)); 78 for(int i=0; i<prime[a[1]].size(); ++i) 79 mark[prime[a[1]][i]] = 1; 80 L[1] = 1; 81 cL[1].push_back(1); 82 for(int i=2;i<=n;++i) 83 { 84 int pos = 0; 85 for(int j=0; j<prime[a[i]].size(); ++j) 86 { 87 pos = max(pos,mark[prime[a[i]][j]]); 88 mark[prime[a[i]][j]] = i; 89 } 90 L[i] = pos + 1; 91 cL[L[i]].push_back(i); 92 } 93 for(int i=2;i<N;++i)mark[i] = n + 1; 94 for(int i=0;i<prime[a[n]].size(); ++i) 95 mark[prime[a[n]][i]] = n; 96 R[n] = n; 97 for(int i=n-1;i>=1;--i) 98 { 99 int pos = n + 1; 100 for(int j=0;j<prime[a[i]].size(); ++j) 101 { 102 pos = min(pos,mark[prime[a[i]][j]]); 103 mark[prime[a[i]][j]] = i; 104 } 105 R[i] = pos - 1; 106 } 107 } 108 int main() 109 { 110 int n,m; 111 getPrime(); 112 while(scanf("%d%d",&n,&m),n+m) 113 { 114 memset(tree,0,sizeof(tree)); 115 memset(lazy,0,sizeof(lazy)); 116 for(int i=1;i<=n;++i) 117 { 118 scanf("%d",&a[i]); 119 cL[i].clear(); 120 } 121 init(n); 122 for(int i=0;i<m;++i) 123 { 124 scanf("%d%d",&q[i].l,&q[i].r); 125 q[i].id = i; 126 } 127 sort(q,q+m); 128 int cur = 0; 129 //枚举左端点 130 for(int i=1;i<=n;++i) 131 { 132 //当左端点为i时,使得所有L[j] = i的数都在各自的区间[j,R[j]] 133 //所以在[j,R[j]]区间+1 134 for(int j=0;j<cL[i].size(); ++j) 135 update(1,n,1,cL[i][j],R[cL[i][j]],1); 136 //当询问的左端点为i时, 137 while(q[cur].l==i) 138 { 139 //只要询问右端点的值就行了,因为每个数都在自己能生效的区间里面+1了 140 ans[q[cur].id] = query(1,n,1,q[cur].r); 141 cur++; 142 } 143 //要走过第i个数了,所以第i个数不再生效了,所以将[i,R[i]]区间-1 144 update(1,n,1,i,R[i],-1); 145 } 146 for(int i=0;i<m;++i) 147 printf("%d ",ans[i]); 148 } 149 return 0; 150 }