HDU - 4777 Rabbit Kingdom 预处理 + 树状数组(区间更新点查询)

传送门:HDU4777

题意:给出一个长度为n的序列和m个询问,每次询问求[l, r]区间内和区间内其他数都互质的数有多少个。

思路:对于每个位置的数先向左向右预处理出最长互质区间L[i], R[i],然后将所有询问记录下来按左端点排序,遍历每个位置pos,若该位置为某个L[i],则将树状数组 i 位置+1,R[i] 位置 - 1,若某个询问Ql == pos,则该询问就可以直接计算出答案了,这一轮处理完以后,剩余的询问的Ql都大于 i ,因此i位置的数就不会对后续询问产生影响了,则应该将L[i] - 1, R[i] + 1,继续进行下一轮处理。

注意预处理的时候的技巧,记录每个出现过的素因子的最新位置,对于一个位置上的数,其所有素因子的最大位置就是其L[i], R[i]同理可以求得。

本题树状数组的更新和查询可能不是很好理解,推荐一个线段树版的:点击打开链接  搞懂了再研究树状数组的就好多了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define pi acos(-1)
#define MAXN 200010
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
int n;
vector<int> factor[MAXN];
void init()
{
	for(int i = 2; i < MAXN; i++)
	if(factor[i].empty())
	for(int j = i; j < MAXN; j += i)
	factor[j].push_back(i);
}
int a[MAXN], L[MAXN], R[MAXN], pos[MAXN], bit[MAXN];
vector<int> p[MAXN];// p[i] := 以i为区间左端点的数的位置 
void calc()
{
	int u, v;
	fill(pos, pos + MAXN, 0);
	for(int i = 1; i <= n; i++)
	{
		L[i] = 1;
		for(int j = 0; j < factor[a[i]].size(); j++)
		{
			u = factor[a[i]][j];
			L[i] = max(L[i], pos[u] + 1);
			pos[u] = i;
		}
		p[L[i]].push_back(i);
	}
	fill(pos, pos + MAXN, n + 1);
	for(int i = n; i >= 1; i--)
	{
		R[i] = n + 1;
		for(int j = 0; j < factor[a[i]].size(); j++)
		{
			u = factor[a[i]][j];
			R[i] = min(R[i], pos[u]);
			pos[u] = i;
		}
	}
}
void add(int i, int x)
{
	while(i <= n)
	{
		bit[i] += x;
		i += i & -i; 
	}
}
int sum(int i)
{
	int res = 0;
	while(i)
	{
		res += bit[i];
		i -= i & -i;
	}
	return res;
}
struct query{
	int l, r, id;
	bool operator < (query a) const
	{
		return l < a.l;
	}
}Q[MAXN];
int ans[MAXN];
int main()
{
	int q;
	init();
	while(scanf("%d %d", &n, &q), n + q)
	{
		memset(bit, 0, sizeof(bit));
		for(int i = 1; i <= n; i++)
		{
			scanf("%d", a + i);
			p[i].clear();
		}
		calc();
		for(int i = 0; i < q; i++)
		{
			scanf("%d %d", &Q[i].l, &Q[i].r);
			Q[i].id = i;
		}
		sort(Q, Q + q);
		int now = 0;
		for(int i = 1; i <= n; i++)
		{
			for(int j = 0; j < p[i].size(); j++)
			{
				add(p[i][j], 1);
				add(R[p[i][j]], -1);
			}
			while(now < q && Q[now].l == i)
			{
				ans[Q[now].id] = sum(Q[now].r) - sum(Q[now].l - 1);
				now++; 
			}
			add(R[i], 1);
add(L[i], -1);
			if(now == q) break;
		}
		for(int i = 0; i < q; i++)
		printf("%d\n", ans[i]);
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值