hdu 4777 Rabbit Kingdom

数据结构

题意:给定10w个数,给出10w个区间询问,问区间中和区间上其

他所有数都互质的数有几个。

很容易想到的是先预处理出每个元素i左边与他互质的到哪儿l[i],右边于他互质的到哪儿r[i],比如2 1 4,l[0]=1,r[0]=2;l[1]=1,r[1]=3,l[2]=2,r[2]=3;

很自然的想法是用树状数组维护区间,在l[i]处+1(其实应该是i),r[i]+1处-1,但这样会有问题,对 2 1 4 3来说查询2 3的时候第0个数还会影响答案

接着很自然的想到把查询离线处理,有序化对于访问过的点,把这个点恢复

还用到一个很神奇的东西,把i在l[i]处用边表存下来,这时候访问到l[i]的时候,就能忽略qr的影响,而把所有的满足左边性质的点都更新进去,这时候就可以用区间减法了

设一个不断移动的扫描线,当扫到一个点的时候,把这个点恢复,扫到一个查询的左区间的时候,把能更新的点都进行更新



#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define Debug(x) (cerr << #x << " = " << (x) << endl)
#define Debug2(x, y) (cerr << #x << " = " << (x) << ", " << #y << " = " << (y) << endl)

typedef long long ll;
typedef pair<int,int> pii;
const int mod  = 1e9+7;
ll powmod(ll a,ll b) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll powmod(ll a,ll b,ll mod) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

const int inf  = 0x3f3f3f3f;   
const int maxn = 200005;

bool isprime[maxn];
int pr[maxn/10],pn;

void init(){
	memset(isprime,true,sizeof(isprime));
	pn = 0;
	for(int i=2;i<maxn;i++){
		if(isprime[i]){
			pr[pn++] = i;
			for(int j=2;i*j<maxn;j++){
				isprime[i*j] = false;
			}
		}
	} 
	//Debug(pn);
}


int c[maxn];
int lowbit(int x){
	return x&(-x);
}
void update(int p,int x){
	while(p < maxn){
		c[p] += x;
		p += lowbit(p);
	}

}
int getsum(int p){
	int ans = 0;
	while(p > 0){
		ans += c[p];
		p -= lowbit(p);
	}
	return ans;
}

int l[maxn];
int r[maxn];
int pre[maxn];


int a[maxn];


int getl(int x,int p){
	int ans = 1;
	p += 1;
	for(int i=0;i<pn&&pr[i]*pr[i]<=x;i++){
		if(x % pr[i] == 0){
			ans = max(ans,pre[i]);
			while(x % pr[i] == 0){
				x /= pr[i];
			}
			pre[i] = p;
		}
	}
	if(x != 1){
		int t = lower_bound(pr,pr+pn,x)-pr;
		ans = max(ans,pre[t]);
		pre[t] = p;
	}
	return ans;
}

	int n,m;
int getr(int x,int p){
	int ans = n;
	p -= 1;
	for(int i=0;i<pn&&pr[i]*pr[i]<=x;i++){
		if(x % pr[i] == 0){
			ans = min(ans,pre[i]);
			while(x % pr[i] == 0){
				x /= pr[i];
			}
			pre[i] = p;
		}
	}
	if(x != 1){
		int t = lower_bound(pr,pr+pn,x)-pr;
		ans = min(ans,pre[t]);
		pre[t] = p;
		//Debug2(t,p);
	}
	return ans;
}

struct op{
	int l,r,id;
	op(){};
	op(int l,int r,int id):l(l),r(r),id(id){};
	void in(int i){
		scanf("%d%d",&l,&r);
		id = i;
	}
	bool operator < (const op &a) const{
		return l < a.l;
	}
}q[maxn];

vector<int> g[maxn];
int ans[maxn];

int main(){
	init();
	while(cin >> n >> m){
		if(n == 0 || m == 0)break;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		for(int i=0;i<pn;i++){
			pre[i] = 1;
		}
		memset(c,0,sizeof(c));
		for(int i=1;i<=n;i++){
			l[i] = getl(a[i],i);
			//update(l[i],1);
		}	
		for(int i=0;i<pn;i++){
			//if(i < 3)Debug(pre[i]);
			pre[i] = n;
		}
		for(int i=n;i>=1;i--){
			r[i] = getr(a[i],i);
			//update(r[i]+1,-1);
		}
		
		for(int i=1;i<=n;i++){
			//Debug2(l[i],r[i]);	
		}
		for(int i=0;i<m;i++){
			q[i].in(i);
		}
		sort(q,q+m);
		for(int i=1;i<=n;i++){
			g[i].clear();
		}
		for(int i=1;i<=n;i++){
			g[l[i]].push_back(i);
		}
		int left = 1;
		for(int j=0;j<g[left].size();j++){
			update(g[left][j],1);
			update(r[g[left][j]]+1,-1);
			//Debug(left);
		}
		for(int i=0;i<m;i++){
			//Debug2(q[i].l,q[i].r);
			while(left < q[i].l){
				update(left,-1);
				update(r[left]+1,1);
				left ++;
				for(int j=0;j<g[left].size();j++){
					update(g[left][j],1);
					update(r[g[left][j]]+1,-1);
					//Debug(left);
				}
				//Debug(left);
			}
			ans[q[i].id] = getsum(q[i].r) - getsum(q[i].l-1);
		}
		for(int i=0;i<m;i++){
			printf("%d\n",ans[i]);
		}
	}
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值