http://acm.hdu.edu.cn/showproblem.php?pid=4777
Rabbit Kingdom
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 296 Accepted Submission(s): 97
n rabbits were numbered form 1 to n. All rabbits' weight is an integer. For some unknown reason, two rabbits would fight each other if and only if their weight is NOT co-prime.
Now the king had arranged the n rabbits in a line ordered by their numbers. The king planned to send some rabbits into prison. He wanted to know that, if he sent all rabbits between the i-th one and the j-th one(including the i-th one and the j-th one) into prison, how many rabbits in the prison would not fight with others.
Please note that a rabbit would not fight with himself.
題目大意:一個數列有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;
}