HDOJ 5726

题意

给你一个数组,询问 [ l , r ] [l,r] [l,r]之间所有数的 gcd ⁡ \gcd gcd以及所有等于这个数的区间数。

题解

区间 gcd ⁡ \gcd gcd可以用线段树和ST表解决,而所有区间gcd等于某个数的个数则需要求解一番。首先我们可以确定,数越多,得出来的gcd肯定会越小,即 gcd ⁡ ( a 1 , a 2 , . . . , a i ) ≤ gcd ⁡ ( a 1 , a 2 , . . . , a i , a i + 1 ) \gcd(a_1,a_2,...,a_i)\le\gcd(a_1,a_2,...,a_i,a_{i+1}) gcd(a1​,a2​,...,ai​)≤gcd(a1​,a2​,...,ai​,ai+1​),也就是说,gcd的前缀和是单调递减的,利用这个性质,可以在 n l o g n nlogn nlogn的时间内预处理出所有g的区间个数。

AC代码:

#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0)
#define _ff(i, a, b) for (int i = a; i <= b; ++i)
#define _f(i, a, b) for (int i = a; i < b; ++i)
using namespace std;
typedef long long ll;

const int N = 1e5 + 5;
ll a[N], f[N][30], n;

map<ll, ll> mp;

void getST() {
	_ff(i, 1, n) f[i][0] = a[i];
	_ff(j, 1, 30) _ff(i, 1, n) {
		if (i + (1<<j) - 1 <= n) f[i][j] = __gcd(f[i][j - 1], f[i + (1<<(j - 1))][j - 1]);
		else break;
	}
}

ll query(int l, int r) {
	ll k = (ll)log2(1.0 * (r - l + 1));
	return __gcd(f[l][k], f[r - (1<<k) + 1][k]);
}

void getMp() {
	mp.clear();
	_ff(i, 1, n) {
		ll g = a[i], j = i;
		while (j <= n) {
			ll l = j, r = n;
			while (l < r) {
				ll mid = (l + r + 1) >> 1;
				if (query(l, mid) == g) l = mid;
				else r = mid - 1;
			}
			mp[g] += l - j + 1;
			j = l + 1;
			g = query(i, j);
		}
	}
}

void solve() {
	cin >> n;
	_ff(i, 1, n) cin >> a[i];
	getST();
	getMp();
	int q, l, r; cin >> q;
	while (q--) {
		cin >> l >> r;
		ll g = query(l, r);
		cout << g << " " << mp[g] << endl;
	}
}

int main() {
	IO;
	int T; cin >> T;
	_ff(i, 1, T) {
		cout << "Case #" << i << ":\n";
		solve();
	}
}

上一篇:《深入理解计算机系统》(CSAPP)实验四 —— Attack Lab


下一篇:ip