CF474F Ant colony

简略题意

给定一个 nn 个元素的序列 aa,并且给定 mm 次询问,每次询问给定区间 [l,r][l,r],设 g=gcd(al,al+1,al+2,,ar)g = \gcd(a_l,a_{l+1}, a_{l+2}, \cdots, a_r),询问 i=lr{1aig0otherwise\sum_{i=l}^r \begin{cases} 1 & a_i \neq g \\ 0 & \text{otherwise} \end{cases} ,即求 (rl+1)(i=lr{1ai=g0otherwise)(r-l+1) - (\sum_{i=l}^r \begin{cases} 1 & a_i=g \\ 0 & \text{otherwise} \end{cases})

解法

给一个离线解法。

第一步,ai109a_i \le 10^9,考虑先离散化。

首先考虑 gcd(al,al+1,al+2,,ar)\gcd(a_l,a_{l+1}, a_{l+2}, \cdots, a_r),明显可以线段树维护,维护的复杂度 O(n(logn+logv))O(n \cdot (\log n + \log v)),因为计算 gcd\gcd 的复杂度是 logn\log n 的,vv 是值域。

维护完之后明显要求 gcd(al,al+1,al+2,,ar)\gcd(a_l,a_{l+1}, a_{l+2}, \cdots, a_r)[l,r][l,r] 中出现的次数,大家都是什么 ST 表或者其他解法,但是出现次数这个问题可以莫队解决。

总结起来,我们对每次询问计算 gcd\gcd,然后莫队维护区间 [l,r][l,r] 中这个 gcd\gcd 出现次数。

接着分析复杂度:

线段树维护的复杂度是 O(n(logn+logv))O(n \cdot (\log n + \log v)),而莫队复杂度 O(nn)O(n \sqrt{n}),所以总复杂度 O(n(logn+logv)+nn)O(n \cdot (\log n + \log v) + n \sqrt{n})(应该没错吧,因为 n,m105n,m \le 10^5,所以我们算复杂度时假设 n,mn,m 同阶,因为不知道 n(logn+logv)n \cdot (\log n + \log v)nnn \sqrt{n} 谁大,所以我们相加。如果有错请指出)。

复杂度好像很大,但是最慢的点也就三百多毫秒,轻松通过。

不得不说有紫题的难度。

代码:

#pragma GCC optimize(2) 
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <utility>
#include <unordered_map>
using namespace std;

const int N = 1e5 + 5;

inline int gcd(int a, int b)
{
	return (b == 0 ? a : gcd(b, a % b));
}

int ans[N], a[N], b[N], n, m, len, cntk[N], real[N];
unordered_map<int, bool> mp;

struct Node
{
	int l, r, gcdn;
}tree[N << 2];

inline void push_up(int u)
{
	tree[u].gcdn = gcd(tree[u << 1].gcdn, tree[u << 1 | 1].gcdn);
}

inline void build(int u, int l, int r)
{
	tree[u] = { l, r };
	if (l == r) tree[u].gcdn = real[a[r]];
	else
	{
		int mid = (l + r) >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
		push_up(u);
	}
}

inline int query(int u, int l, int r)
{
	if (tree[u].l >= l && tree[u].r <= r) return tree[u].gcdn;
	int mid = (tree[u].l + tree[u].r) >> 1;
	int k = 0;
	if (l <= mid)
	{
		k = query(u << 1, l, r);
	}
	if (r > mid)
	{
		k = gcd(k, query(u << 1 | 1, l, r));
	}
	return k;
}

struct Nodeq
{
	int rid, l, r, gcdn;
	bool operator<(const Nodeq& g) const
	{
		int i = l / len, j = g.l / len;
		return (i ^ j ? i < j : i & 1 ? r < g.r : r > g.r);
	}
}q[N];

inline void add(int x)
{
	++cntk[a[x]];
}

inline void del(int x)
{
	--cntk[a[x]];
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%d", &a[i]);
		b[i] = a[i];
		mp[a[i]] = true;
	}
	sort(b + 1, b + n + 1);
	int place = unique(b + 1, b + n + 1) - b - 1;
	for (register int i = 1; i <= n; ++i)
	{
		int k = a[i];
		a[i] = lower_bound(b + 1, b + place + 1, a[i]) - b + 1;
		real[a[i]] = k;
	}
	build(1, 1, n);
	scanf("%d", &m);
	for (register int i = 1; i <= m; ++i)
	{
		register int l, r;
		scanf("%d %d", &l, &r);
		int qp = query(1, l, r);
		q[i] = { i, l, r, qp };
	}
	len = sqrt(m);
	sort(q + 1, q + m + 1);
	register int nl = 1, nr = 0;
	for (register int i = 1; i <= m; ++i)
	{
		int l = q[i].l, r = q[i].r;
		while (nl < l) del(nl++);
		while (nl > l) add(--nl);
		while (nr < r) add(++nr);
		while (nr > r) del(nr--);
		if (!mp[q[i].gcdn]) ans[q[i].rid] = r - l + 1;
		else ans[q[i].rid] = (r - l + 1) - cntk[lower_bound(b + 1, b + place + 1, q[i].gcdn) - b + 1];
	}
	for (register int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
	return 0;
}
posted @   HappyBobb  阅读(1)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示