标签:
题目大意:给N个数,有M个查询,问区间[L,R]之间有多少个数与这个区间内的其他数都互质。
思路:dp显然很难搞,既然是求区间就试试每一个数最大可以对答案产生贡献的区间,即预处理出一个数在(lp,rp)内始终与其他数互质的最大区间
则lp和rp分别为左边和右边第一个与他不互质的数的位置
处理的时候素数打表然后从左到右始终更新对于某一个素因子出现的最右的位置然后更新l,r,可以做到
然后就是把查询按l从小到大排序,这样的话每处理一个新的查询,对于在这个查询的 l 左边的数就可以不用考虑了
然后我们假设先处理左边界为1的查询,很明显如果我们把lp为0的所有位置pos都加上1同时把pos对应的rp减去1,用rp[pos]-1表示
然后用树状数组求这次查询的区间[l,r]的和即为答案
左边界为1的处理出来了,很容易就想到左边界为2,3.....n的处理方式,即与上诉类似
还有一个问题就是当处理的左边界不断增大有则左边界的左边的数会不断被舍去此时就要注意消除掉被舍去的数对于树状数组的影响,设每一步被舍弃的位置为i,则需要让树状数组的rp[i]位置加上1
附代码:
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> #include <iostream> #include <map> #include <queue> #include <stack> #include <cmath> //#pragma comment(linker, "/STACK:102400000,102400000") using namespace std; #define PF(x) cout << "debug: " << x << " "; #define EL cout << endl; #define PC(x) puts(x); typedef long long ll; #define CLR(x, v) sizeof (x, v, sizeof(x)) using namespace std; const int INF = 0x5f5f5f5f; const int N= 1000050; const int maxn=200000+10; int n,m,r[maxn],l[maxn],a[maxn],tree[maxn],pri[maxn],mark[maxn]; vector<int>p[maxn],vt[maxn]; struct st { int l,r,id; } q[maxn]; void prime() { // int sr = sqrt(200000+5+0.5); for(int i = 2; i <= 200000; i++) if(!pri[i]){ p[i].push_back(i); for(int j = i+i; j <= 200000; j+=i) { pri[j]=1; p[j].push_back(i); } } } void pre() { memset(mark,0,sizeof(mark)); for(int i = 0; i <= n; i++) { tree[i]=0; vt[i].clear(); l[i]=0; r[i]=n+1; } for(int i= 1; i <= n; i++) { int lv=p[a[i]].size(); //cout<<lv<<" "<<a[i]<<" "<<i<<endl; for(int j= 0; j < lv; j++) { int x = p[a[i]][j]; if(!mark[x]) { mark[x] = i; continue; } if(r[mark[x]] == n+1) r[mark[x]] = i; l[i] = max(l[i],mark[x]); mark[x]=i; } vt[l[i]].push_back(i); } // for(int i= 1;i <= n;i++) // cout<<l[i]<<" "<<r[i]<<endl; } bool cmp(st x,st y) { return x.l<y.l; } void add(int k , int num) { while(k <= n) { tree[k] += num; k += k&(-k); } } int Sum(int k) { int sum = 0; while(k > 0) { sum += tree[k]; k -= k&(-k); } return sum; } int main() { // freopen("in.txt","r",stdin); prime(); while(~scanf("%d%d",&n,&m)) { //cout<<"r"<<endl; if(n == m&&m == 0) break; int i,j; for(i = 1; i <= n; i++) scanf("%d",&a[i]); for(i = 1; i <= m; i++) { scanf("%d%d",&q[i].l,&q[i].r); // cout<<q[i].l<<" "<<q[i].r<<endl; q[i].id=i; } pre(); sort(q,q+1+n,cmp); int cnt=1,ans[maxn]; for(int i = 0; i <= n; i++) { while(q[cnt].l == i) { ans[q[cnt].id] = Sum(q[cnt].r)-Sum(q[cnt].l-1); //cout<<ans[q[cnt].id]<<" "<<q[cnt].id<<endl; cnt++; } int x=vt[i].size(); for(j = 0;j < x;j++){ int v=vt[i][j]; add(v,1); add(r[v],-1); } if(r[i]!=n+1) add(r[i],1); } for(i = 1;i <= m;i++) cout<<ans[i]<<endl; } return 0; }
hdu4777 Rabbit Kingdom 树状数组+区间操作+素数打表
标签:
原文地址:http://www.cnblogs.com/shimu/p/5799000.html