[HDU]5726

询问好搞 线段树即可 

问题在于如何统计区间个数 需要注意到以i开头的区间每次gcd发生变化gcd的值都至少除以二(最小因子为2) 所以最多变化logn次 只需快速找到这logn个变化点即可快速计算每个gcd值对应的区间个数并用map保存

查找过程可以利用选段树 不断查找由i开始使当前gcd值小于目标值的位置 对于每一个i至多logn次 线段树操作复杂度logn 总复杂度nlogn^2

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#define SF scanf
#define PF printf
using namespace std;
typedef long long LL;
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
const int MAXN = 100000;
const int MOD = 1e9 + 7;
const int INF = 1234567890;
int n;
int gcd(int a, int b) {
	while(b) {
		int r = a % b;
		a = b;
		b = r;
	}
	return a;
}
int A[MAXN+10];
struct Seg_Tree {
	int g[MAXN*4+10];
	void build(int i, int l, int r) {
		if(l == r) { g[i] = A[l]; return ; }
		int mid = (l+r) >> 1;
		build(i*2, l, mid);
		build(i*2+1, mid+1, r);
		g[i] = gcd(g[i*2], g[i*2+1]);
	}
	int query(int i, int l, int r, int L, int R) {
		if(L <= l && r <= R) return g[i];
		int mid = (l+r) >> 1;
		int ls = 0, rs = 0;
		if(L <= mid) ls = query(i*2, l, mid, L, R);
		if(R > mid) rs = query(i*2+1, mid+1, r, L, R);
		if(!ls) swap(ls, rs);
		return gcd(ls, rs);
	}
	int search(int i, int l, int r, int L, int R, int cur, int &GCD) {
		if(L <= l && r <= R) {
			if(gcd(g[i], GCD) < cur) {
				if(l == r) return l;
				else {
					int mid = (l+r) >> 1, ls = 0, rs = 0;
					ls = search(i*2, l, mid, L, R, cur, GCD);
					if(ls) return ls;
					rs = search(i*2+1, mid+1, r, L, R, cur, GCD);
					return rs;
				}
			}
			else { 
				GCD = gcd(GCD, g[i]);
				return 0;
			}
		}
		int mid = (l+r) >> 1, ls = 0, rs = 0;
		if(L <= mid) ls = search(i*2, l, mid, L, R, cur, GCD);
		if(ls) return ls;
		if(R > mid) rs = search(i*2+1, mid+1, r, L, R, cur, GCD);
		return rs;
	}
} seg;
map <int, LL> M;
int main() {
	int T = read();
	for(int kase = 1; kase <= T; kase++) {
		M.clear();
		n = read();
		for(int i = 1; i <= n; i++) A[i] = read();
		seg.build(1, 1, n);
		for(int i = 1; i <= n; i++) {
			int g = seg.query(1, 1, n, i, n);
			for(int Now = i, cur = A[i]; Now <= n; ) {
				if(g == cur) { M[g] += n - Now + 1; break; }
				int GCD = A[i];
				int Nex = seg.search(1, 1, n, i, n, cur, GCD);
				M[cur] += Nex - Now;
				Now = Nex;
				cur = gcd(A[Now], cur);
			}
		}
		printf("Case #%d:\n", kase);
		int Q = read();
		for(int i = 1; i <= Q; i++) {
			int l = read(), r = read();
			int g = seg.query(1, 1, n, l, r);
			PF("%d %I64d\n", g, M[g]);
		}
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值