HDU-4777 Rabbit Kingdom(区间更新求和)

题目大意:给一个n个整数的数列,q次询问,每次询问区间[l,r]中与区间中其它数互质的数的个数.。

题目分析:离线处理,这里以询问区间的左端点从小到大的顺序为例。为了叙述方便,用f(l,r)表示区间[l,r]中与区间中其它数互质的数的个数.。每次用线段树或树状数组维护以 a(i)(1<=i<=n) 为左端点的所有区间的 f 值的前缀和。左端点从1~n,每变化一次,便做一次更新操作。这样,f(l,r)=sum(l)-sum(r+1)。对于数列中的每个元素a(i),预处理出其左边第一个不与他互质的数li(i),同样预处理出ri(i)。当左端点由a(i)变为a(i+1)时,要将区间[i+1,ri(i)-1]的 f 值都减1,同理,如果存在j>i并且li(j)=i,那么就要将区间[i+1,ri(j)-1]的 f 值都加1。

 

代码如下(用树状数组维护):

# include<iostream>
# include<cstdio>
# include<map>
# include<set>
# include<vector>
# include<cstring>
# include<algorithm>
using namespace std;

const int N=200000;

struct Node
{
	int id,l,r;
};
Node nde[N+1];
int a[N+1];
int li[N+1];
int ri[N+1];
int mark[N+1];
int ans[N+1];
int sum[N+1];
vector<int>edge[N+1];
vector<int>v[N+1];
int n;

bool comp(const Node &a,const Node &b)
{
	return a.l<b.l;
}

void init()
{
	for(int i=2;i<=N;++i)
		for(int j=i;j<=N;j+=i)
			v[j].push_back(i);
}

int lowbit(int x)
{
	return x&(-x);
}

void add(int x,int val)
{
	while(x>=1){
		sum[x]+=val;
		x-=lowbit(x);
	}
}

int getSum(int x)
{
	int res=0;
	while(x<=n){
		res+=sum[x];
		x+=lowbit(x);
	}
	return res;
}

int main()
{
	init();
	int m;
	while(scanf("%d%d",&n,&m)&&(n+m))
	{
		for(int i=1;i<=n;++i){
			scanf("%d",a+i);
			edge[i].clear();
		}
		memset(mark,0,sizeof(mark));
		for(int i=1;i<=n;++i){
			li[i]=0;
			for(int j=0;j<v[a[i]].size();++j){
				li[i]=max(li[i],mark[v[a[i]][j]]);
				mark[v[a[i]][j]]=i;
			}
		}
		memset(mark,1,sizeof(mark));
		for(int i=n;i>=1;--i){
			ri[i]=n+1;
			for(int j=0;j<v[a[i]].size();++j){
				ri[i]=min(ri[i],mark[v[a[i]][j]]);
				mark[v[a[i]][j]]=i;
			}
		}
		
		memset(sum,0,sizeof(sum));
		for(int 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(int i=1;i<=m;++i){
			scanf("%d%d",&nde[i].l,&nde[i].r);
			nde[i].id=i;
		}
		
		int id=1;
		sort(nde+1,nde+m+1,comp);
		
		for(int i=1;i<=n&&id<=m;++i){
			while(nde[id].l==i){
				ans[nde[id].id]=(getSum(nde[id].l)-getSum(nde[id].r+1));
				++id;
			}
			add(i,-1);
			if(ri[i]<=n) add(ri[i],1);
			for(int j=0;j<edge[i].size();++j){
				int x=edge[i][j];
				add(x,1);
				if(ri[x]<=n) add(ri[x],-1);
			}
		}
		for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
	}
	return 0;
}

  

posted @ 2016-08-01 20:06  20143605  阅读(390)  评论(0编辑  收藏  举报