CF474F Ant colony 解题报告

题目大意:

有一个长度为 n n n 的整数序列,有 q q q 次询问,每次询问给出一个区间 [ l , r ] [l,r] [l,r] 求出所有不能被区间所有数整除的数量。

题解:

考虑询问区间中被区间所有数的数量。

首先这类数一定是询问区间中最小的数,且必定为这段区间最大公因数的因数,但又因为这类数是区间最小值,且区间 gcd ⁡ \gcd gcd 必定小于等于区间最小值,故这类数一定为区间 gcd ⁡ \gcd gcd

所以我们可以用线段树统计出区间最小值,区间最小值个数,以及区间 gcd ⁡ \gcd gcd.

在每次询问统计答案时,若区间最小值为区间 gcd ⁡ \gcd gcd,则答案累加区间最小值最小值的个数即可。

线段树每次查询以及建树都是 O ( log ⁡ n ) O(\log n) O(logn) 的,所以总复杂度为 O ( q log ⁡ n ) O(q \log n) O(qlogn).

参考代码:

#include<bits/stdc++.h>
using namespace std;
int read() {
	int f = 1, x = 0; char ch = getchar();
	while((ch < '0' || ch > '9') && ch != '-') ch = getchar();
	if(ch == '-') f = -1, ch = getchar();
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * f;
}
const int N = 1e5 + 10;
struct Segment {
	int g, cnt, minn;
}t[N << 2]; 
int n, q, a[N];
void pushup(int p) { //更新节点的值
	t[p].minn = min(t[p << 1].minn, t[p << 1 | 1].minn);
	t[p].g = __gcd(t[p << 1].g, t[p << 1 | 1].g);
	if(t[p << 1].minn == t[p << 1 | 1].minn) t[p].cnt = t[p << 1].cnt + t[p << 1 | 1].cnt;
	else if(t[p << 1].minn < t[p << 1 | 1].minn) t[p].cnt = t[p << 1].cnt;
	else if(t[p << 1 | 1].minn < t[p << 1].minn) t[p].cnt = t[p << 1 | 1].cnt; 
}
void build(int p, int l, int r) { //建树
	int mid = l + r >> 1;
	if(l == r) {
		t[p].cnt = 1;
		t[p].g = a[l];
		t[p].minn = a[l]; 
	}
	else {
		build(p << 1, l, mid);
		build(p << 1 | 1, mid + 1, r);
		pushup(p);	
	}
}
Segment query(int p, int l, int r, int x, int y) { // 查询
	if(l > y || r < x) return (Segment){0, 0, 1000000000};
	if(x <= l && r <= y) return t[p];
	int mid = l + r >> 1;
	Segment lc = query(p << 1, l, mid, x, y);
	Segment rc = query(p << 1 | 1, mid + 1, r, x, y);
	Segment tmp;
	tmp.minn = min(lc.minn, rc.minn);
	tmp.g = __gcd(lc.g, rc.g);
	if(lc.minn == rc.minn) tmp.cnt = lc.cnt + rc.cnt;
	else if(lc.minn < rc.minn) tmp.cnt = lc.cnt;
	else if(rc.minn < lc.minn) tmp.cnt = rc.cnt; 
	return tmp;
}
int main() {
	n = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	build(1, 1, n);
	q = read();
	while(q --) {
		int l, r;
		l = read(), r = read();
		Segment answer = query(1, 1, n, l, r);
		int ans = 0;
		if(answer.minn == answer.g) ans = answer.cnt; //处理答案
		cout << r - l + 1 - ans << endl;
	}
	return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值