http://acm.hdu.edu.cn/showproblem.php?pid=4777
题目大意:一个数列有n个数,然后有m个询问,每个询问 l,r表示问你区间[l,r]中有多少个数与除自己之外的其他数互质。
思路:啊啊啊,太弱了,自己想了好久都没有搞出来,参考了董适大牛的解题报告才弄明白。。。
董神的博客:http://blog.csdn.net/dslovemz/article/details/15236929
首先将每个数左边第一个与它不互质和右边第一个与它不互质的数的位置记录下来,设为li[i],和ri[i]。这个简单直接跳过。然后将询问离线处理,按l升序排序,我们从左到右依次处理询问,接下来我们要维护一个数列,使得我们在考虑左端点为L的询问时,以L为起始的前缀和sum[l,r]表示区间[l,r]中满足题目条件的数的数量,下面来看看怎么维护这个数列。首先当然将该数列初始化为0,然后我们将li[i]<1(也就是左边没有与他不互质的数)的位置i加上1,ri[i]位置-1.
这里解释一下,因为一开始我们讨论的是区间左端点在1的询问(不管是否真的有这组询问),那么对于li[i]<1的位置i,在区间[i,r[i]-1]它都符合要求,在区间[r[i],n]都不符合要求,所以我们就要在位置i加上1,在位置ri[i]减去1。考虑完左端点为1的询问后,我们就要右移左端点,假设当前的左端点是left,我们下一步要考虑左端点为left+1的询问,那么我们要把left位置的数取消,即把left位置-1,将ri[left]位置+1。
接下来是关键的一部,因为我们下一步是要考虑左端点在left+1的询问,那么对于li[i]==left的位置i,从left之后它就有可能被算上,所以我们还要把所有li[i]==left的i全+1,并且将ri[i]全-1。这可以在预处理li[i]的时候建立边表或者用vector存。之后将left+1,最后统计答案的时候就将区间求和即可得到答案,要快速求区间和可以用树状数组或线段树实现,还是很好写的。以下是代码,仅供参考。。。
#include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> #include <vector> #define inf 2100000000 #define maxn 200010 using namespace std; struct ask { int l,r,num; }query[maxn]; bool cmp(ask a,ask b) { return a.l<b.l; } int li[maxn],ri[maxn],a[maxn],ans[maxn],vis[maxn]; int c[maxn]; int n,m; int lowbit(int x) { return x&(-x); } void add(int x,int val) { while(x>0) { c[x]+=val; x-=lowbit(x); } } int getsum(int x) { int sum=0; while(x<=n) { sum+=c[x]; x+=lowbit(x); } return sum; } vector<int> vec[maxn]; vector<int> edge[maxn]; void init() { int i,j; for(i=2;i<=200000;i++) { for(j=i;j<=200000;j+=i) { vec[j].push_back(i); } } } int main() { //freopen("dd.txt","r",stdin); init(); while(scanf("%d%d",&n,&m)&&(n+m)) { int i,j; for(i=1;i<=n;i++) scanf("%d",&a[i]); memset(vis,0,sizeof(vis)); for(i=1;i<=n;i++)//预处理li { int siz=vec[a[i]].size(),po=0; for(j=0;j<siz;j++) { po=max(po,vis[vec[a[i]][j]]); vis[vec[a[i]][j]]=i; } li[i]=po; } memset(vis,1,sizeof(vis)); for(i=n;i>=1;i--)//预处理ri { int siz=vec[a[i]].size(),po=n+1; for(j=0;j<siz;j++) { po=min(po,vis[vec[a[i]][j]]); vis[vec[a[i]][j]]=i; } ri[i]=po; } memset(c,0,sizeof(c)); for(i=1;i<=n;i++) { if(li[i]) { edge[li[i]].push_back(i); } else { add(i,1); if(ri[i]<=n) add(ri[i],-1); } } for(i=1;i<=m;i++) { scanf("%d%d",&query[i].l,&query[i].r); query[i].num=i; } sort(query+1,query+m+1,cmp); int num=1; query[m+1].l=-1; for(i=1;i<=n;i++) { while(query[num].l==i) { ans[query[num].num]=(getsum(query[num].l)-getsum(query[num].r+1)); num++; } add(i,-1); if(ri[i]<=n) add(ri[i],1); int siz=edge[i].size(); for(j=0;j<siz;j++) { int x=edge[i][j]; add(x,1); if(ri[x]<=n) add(ri[x],-1); } edge[i].clear(); } for(i=1;i<=m;i++) { printf("%d\n",ans[i]); } } return 0; }