CF1516-D---rmq+dp(st表)

This time Baby Ehab will only cut and not stick. He starts with a piece of paper with an array a of length n written on it, and then he does the following:

he picks a range (l,r) and cuts the subsegment al,al+1,…,ar out, removing the rest of the array.
he then cuts this range into multiple subranges.
to add a number theory spice to it, he requires that the elements of every subrange must have their product equal to their least common multiple (LCM).
Formally, he partitions the elements of al,al+1,…,ar into contiguous subarrays such that the product of every subarray is equal to its LCM. Now, for q independent ranges (l,r), tell Baby Ehab the minimum number of subarrays he needs.

Input
The first line contains 2 integers n and q (1≤n,q≤105) — the length of the array a and the number of queries.

The next line contains n integers a1, a2, …, an (1≤ai≤105) — the elements of the array a.

Each of the next q lines contains 2 integers l and r (1≤l≤r≤n) — the endpoints of this query’s interval.

Output
For each query, print its answer on a new line.

cf题目
题目大意:
给定数组a[n],现有q个询问Lq,Rq
每个询问代表在[Lq,Rq]的子序列至少要分割成多少个连续子序列,才能保证每个序列内数互质

题解:
大思维题(看了大佬的题解我直呼神奇)
打ST表,st[i][j]表示以i为开头的连续2^j个互质区间的末尾数据下标
为了打表,首先要算出以每个数a[i]为开头的第一个连续互质区间(的末尾),如果暴力,复杂度n^2,不可取,为了降低时间复杂度,先用o(n)打出后续互质区间sep(表示sep[i-1]到sep[i]之间的连续区间互质)然后再算每个数的最长连续后置区间就可以直接从其对应的sep开始(将每个数打表的``时间复杂度降低至 根号n)
然后再用rmq打出总的st表

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<cstdio>
#include<cstring>
#define me(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
const ll N = 2e5 + 7, inf = 1e18;
ll n, q, l, r, a[N], rmq[N][40], nxt[N];
map<ll,ll>mp;//map若直接用下标查询,会创建新key,导致clear爆TLE
//这里用count查询key是否存在,完美解决此问题
vector<ll>sep;

ll gcd(ll x, ll y) {
	if (!y)return x;
	return gcd(y, x % y);
}
bool check(ll x) {//检查栈(储存的是前面一些元素的质因子)内元素是否与新元素互质
	for (ll i = 2; i * i <= x; i++) {
		if (x % i)continue;
		if (mp.count(i))return false;
		mp[i] = 1;
		while (x % i == 0)x /= i;
	}
	if (x>1) {
		if (mp.count(x))return false;
		mp[x] = 1;
	}
	return true;
}

void set_st() {
	sep.push_back(0), sep.push_back(1);
	for (ll i = 1; i <= n; i++) 
		if (!check(a[i])) {
			mp.clear();//若不互质,清空栈,将当前元素作为分割点(与之前元素不互质)
			//重建栈
			check(a[i]);
			sep.push_back(i);
		}
	sep.push_back(n + 1);
	ll l, r = n + 1;
	for (l = n; l >= 1; l--) {//nxt数组记录此元素的顺序互质区间的最后元素
		ll pr = upper_bound(sep.begin(), sep.end(), l) - sep.begin();
		//已知以sep[pr]与前区间(sep[pr-1]至sep[pr])的一些元素不互质
		//从sep[pr]开始,节省打表时间
		for (ll i = sep[pr]; i <= r; i++) 
			if (gcd(a[l], a[i]) != 1) {
				r = i;
				break;
			}
		nxt[l] = r;
	}
	//rmq[i][j]表示以i为首的第2^j的互质区间的区间末尾元素下标
	for (ll i = 1; i <= n; i++) rmq[i][0] = nxt[i];
	for (ll j = 0; j <= 20; j++) rmq[n + 1][j] = n + 1;
	for (ll j = 1; j <= 20; j++)
		for (ll i = 1; i <= n; i++)
			rmq[i][j] = rmq[rmq[i][j - 1]][j - 1];
}

int main() {
	cin >> n >> q;
	for (ll i = 1; i <= n; i++) scanf("%lld", &a[i]);
	set_st();
	while (q--) {
		scanf("%lld %lld", &l, &r);
		ll ans = 0, x = l;
		for (ll j = 20; j >= 0; j--) 
			if (rmq[x][j] <= r) {
				ans += (1ll << j);
				x = rmq[x][j];
			}
		cout << (ans + 1) << "\n";
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值