hdu 4777 Rabbit Kingdom (樹狀陣列+離線處理)
阿新 • • 發佈:2018-12-15
題目連結:哆啦A夢傳送門
題意:給一串n個數字,m個詢問,每次詢問的區間中,與其他元素都互素的數字有多少個?
參考連結:https://www.cnblogs.com/kuangbin/p/3416181.html
https://www.cnblogs.com/shuguangzw/p/5272595.html
題解:這又是讓你找互素,老辦法,每次讓你找有關互素的,你都不要去找,而去找不互素的,因為不互素我們可以通過質因子分解來解決。這題也是。
我們先每個數預處理出L,R區間,表示左右和這個數不互質的位置。這個只要從左到右和從右到左掃描一遍,分解質因素,找下一個質因素的位置。
然後對於每個查詢進行離線處理,按照右端點排序。
更新的過程是這樣的
(1)對於剛加入點x,樹狀陣列L[x]位置+1 把這個定義為左更新
(2)對於所有R[i]=x的點,樹狀陣列L[i]位置-1,i位置+1 把這個定義為右更新
(3)查詢是詢問區間 l->r的和
至於為什麼要這樣更新,這裡題解用的方法確實很妙,實在很難簡單用三言兩語將清楚,我建議模擬一遍,不過程式碼裡還是寫了點註釋。
#include<cstdio> #include<algorithm> #include<cstring> #include<vector> using namespace std; const int maxn=200010; int prime[maxn],tot; bool book[maxn]; void getprime() { memset(book,1,sizeof(book)); book[1]=tot=0; for(int i=2;i<=200000;i++) { if(book[i]) prime[++tot]=i; for(int j=1;j<=tot&&i*prime[j]<=200000;j++) { book[i*prime[j]]=0; if(i%prime[j]==0) break; } } } int factor[100][2]; int fatcnt; void getfactor(int x) { fatcnt=0; int tmp=x; for(int i=1;prime[i]<=tmp/prime[i];i++) { factor[fatcnt][1]=0; if(tmp%prime[i]==0) { factor[fatcnt][0]=prime[i]; ///儲存素因子 while(tmp%prime[i]==0){ factor[fatcnt][1]++; ///儲存素因子個數 tmp/=prime[i]; } fatcnt++; } } if(tmp!=1){ factor[fatcnt][0]=tmp; factor[fatcnt++][1]=1; } // return fatcnt; } int L[maxn],R[maxn]; int a[maxn]; int b[maxn]; int n,m; int c[maxn]; void add(int i,int val) { if(i==0) return; while(i<=n){ c[i]+=val; i=i+(i&(-i)); } } int sum(int i) { int sum=0; while(i>0) { sum+=c[i]; i-=(i&(-i)); } return sum; } vector<int> vec[maxn]; struct node{ int l,r,index; }num[maxn]; bool cmp(node x,node y){ return x.r<y.r;} int ans[maxn],pp[maxn][15]; int main() { getprime(); // for(int i=1;i<=10;i++) // printf("%d ",prime[i]); // puts(""); while(scanf("%d%d",&n,&m)) { if(m==0&&n==0) break; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) { scanf("%d%d",&num[i].l,&num[i].r); num[i].index=i; } for(int i=1;i<maxn;i++) b[i]=n+1; for(int i=n;i>=1;i--) { getfactor(a[i]);///得到a[i]的質因子 R[i]=n+1; ///假設為最右端+1 pp[i][0]=fatcnt; ///儲存a[i]的素因子個數 for(int j=0;j<fatcnt;j++) { R[i]=min(R[i],b[factor[j][0]]); b[factor[j][0]]=i;///標記質因子的位置 pp[i][j+1]=factor[j][0];///儲存a[i]的素因子 } } for(int i=1;i<maxn;i++) b[i]=0; for(int i=1;i<=n;i++) { L[i]=0; ///假設為最左端減1 fatcnt=pp[i][0]; for(int j=0;j<fatcnt;j++) { int item=pp[i][j+1]; L[i]=max(L[i],b[item]); b[item]=i; } } // for(int i=1;i<=n;i++) // printf("L[%d]=%d ",i,L[i]); // puts(""); // for(int i=1;i<=n;i++) // printf("R[%d]=%d ",i,R[i]); // puts(""); sort(num+1,num+1+m,cmp); memset(c,0,sizeof(c)); for(int i=1;i<=n;i++){ c[i]=0; vec[i].clear(); } for(int i=1;i<=n;i++) vec[R[i]].push_back(i); ///儲存以R[i]為右端的點集 int id=1; for(int i=1;i<=m;i++) { while(id<=n&&id<=num[i].r) { ///我們在L[id]的位置加1,那麼說明在當前以num[i].r為右端點的區間 ///不互質數加1 add(L[id],1); int SIZE=vec[id].size(); for(int j=0;j<SIZE;j++){///說明有以R[id]為界的點 int v=vec[id][j]; add(v,1);///我們在v位置加1,因為某個區間要查詢到v,故要在這加1 add(L[v],-1);///我們在L[v]位置減1,因為前面已經加了1,故在這要減1 } id++; } int len=sum(num[i].r)-sum(num[i].l-1); ans[num[i].index]=num[i].r-num[i].l+1-len; } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); } return 0; }