hdu(预处理+线段树)

给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 }
原文地址:https://www.cnblogs.com/justPassBy/p/4886191.html