连续区间的最大公约数

时间限制:C/C++ 4秒,其他语言8秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述 

给一个数列共n(n<=100,000)个数,a1,a2,...,an.(0<=ai<=1000,000,000).有q(q<=100,000)个询问。每个询问为l,r(1<=l<=r<=n).求gcd(al,al+1,...,ar).
再求区间[l,r]的子区间中(l<=l'<=r'<=r)满足gcd(al,al+1,...,ar) = gcd(al',al'+1,...ar')的子区间个数.

输入描述:

第一行一个数T表示数据组数
第二行一个数n
接下来一行n个数,a1,a2,...,an
接下一行一个数q
接下来一行2个数l和r。

输出描述:

首先输出一行“Case #:t”,t代表当前是第几组数据。
接下来q行,每行输出2个数,第一个是gcd(al,al+1,...,ar),
第二个是区间[l,r]的子区间中(l<=l'<=r'<=r)满足gcd(al,al+1,...,ar) = gcd(al',al'+1,...ar')的子区间个数。
示例1

输入

2
5
1 2 4 6 7
4
1 5
2 4
3 4
4 4
5
1 2 4 2 1
6
1 1
1 3
2 2
2 3
2 4
3 3

输出

Case #1:
1 8
2 4
2 1
6 1
Case #2:
1 1
1 3
2 1
2 2
2 5
4 1


解题思路:

可以线段树暴力上,不过需要保存的东西比较多,需要保存区间的gcd,区间内和区间gcd不一样的子区间个数,区间长度,固定区间左端点向右拓展gcd的情况,固定区间右端点向左拓展gcd的情况

以每一个位置 i 作为区间右端点,左端点从[1, i] 移动的过程中,区间 gcd 的种类不会超过 log(n) 种,可以把这些处理出来,记录成四元组(L1, L2, R, g),表示区间左端点在[L1, L2],右端点在 R 的所有区间,最大公约数均为 g。所有的四元组已经包含了所有的子区间。那么对于每一次的询问(L, R, g),只需在和这一次询问拥有相同 g 的四元组中寻找答案,去计算每一个四元组做出的贡献,可以按 R 排序,然后利用线段树区间修改、求区间和来处理。


方法一:

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <string>  
#include <algorithm>  
#include <map>  
#include <set>
#include <stack>
#include <queue>  
#include <vector>  
#include <bitset>  
#include <functional>  

using namespace std;

#define LL long long
const int INF = 0x3f3f3f3f;

LL a[100009];
int n, q, l, r;

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

struct node
{
	vector<pair<int, int> >l, r;
	LL cnt;
	int len, x;
	static void Merge(vector<pair<int, int> > &a, const vector<pair<int, int> >&b)
	{
		for (int i = 0; i < b.size(); i++)
		{
			pair<int, int>tmp = b[i];
			if (tmp.first % a.back().first == 0) a.back().second += tmp.second;
			else a.push_back(make_pair(gcd(tmp.first, a.back().first), tmp.second));
		}
	}
	static LL solve(const vector<pair<int, int> > &a, const vector<pair<int, int> > &b, const int &x)
	{
		LL ans = 0;
		int sum = 0, Size = b.size() - 1;
		for (int i = 0; i < b.size(); i++) sum += b[i].second;
		for (int i = 0; i < a.size(); i++)
		{
			pair<int, int>tmp = a[i];
			while (Size >= 0 && gcd(tmp.first, b[Size].first) == x)
				sum -= b[Size--].second;
			ans += 1LL * tmp.second * sum;
		}
		return ans;
	}
	node operator +(const node &a)const
	{
		node ans;
		ans.x = gcd(x, a.x);
		ans.cnt = 0;
		ans.cnt += x == ans.x ? cnt : 1LL * (len + 1) * len / 2;
		ans.cnt += a.x == ans.x ? a.cnt : 1LL * (a.len + 1) * a.len / 2;
		ans.len = len + a.len;
		Merge(ans.l = l, a.l);
		Merge(ans.r = a.r, r);
		ans.cnt += solve(r, a.l, ans.x);
		return ans;
	}
}x[100009 << 2];

void build(int k, int l, int r)
{
	if (l == r)
	{
		x[k].l.clear(), x[k].r.clear();
		x[k].l.push_back(make_pair(a[l], 1));
		x[k].r.push_back(make_pair(a[l], 1));
		x[k].x = a[l];
		x[k].cnt = (x[k].len = 1) - 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(k << 1, l, mid);
	build(k << 1 | 1, mid + 1, r);
	x[k] = x[k << 1] + x[k << 1 | 1];
}

node query(int k, int l, int r, int ll, int rr)
{
	if (l >= ll && r <= rr) return x[k];
	int mid = (l + r) >> 1;
	if (mid >= rr) return query(k << 1, l, mid, ll, rr);
	else if (mid < ll) return query(k << 1 | 1, mid + 1, r, ll, rr);
	return query(k << 1, l, mid, ll, rr) + query(k << 1 | 1, mid + 1, r, ll, rr);
}

int main()
{
	int t, cas = 0;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
		build(1, 1, n);
		scanf("%d", &q);
		printf("Case #%d:\n", ++cas);
		while (q--)
		{
			scanf("%d %d", &l, &r);
			node ans = query(1, 1, n, l, r);
			printf("%d %lld\n", ans.x, 1LL * ans.len * (ans.len + 1) / 2 - ans.cnt);
		}
	}
	return 0;
}

方法二:

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <string>  
#include <algorithm>  
#include <map>  
#include <set>
#include <stack>
#include <queue>  
#include <vector>  
#include <bitset>  
#include <functional>  

using namespace std;

#define LL long long
const int INF = 0x3f3f3f3f;

int n, m, l, r;
int x[1000009 << 2], a[1000009];
LL lazy[1000009 << 2], sum[1000009 << 2];

struct node
{
	int l, r, g, id;
	LL ans;
	bool operator < (const node &x)const
	{
		if (g != x.g) return g < x.g;
		return r < x.r;
	}
}b[1000009];

struct node1
{
	int l1, l2, r, g;
	bool operator < (const node1 &x)const
	{
		if (g != x.g) return g < x.g;
		return r < x.r;
	}
}c[1000009];

int gcd(int x, int y)
{
	return x ? gcd(y % x, x) : y;
}

void build(int k, int l, int r)
{
	if (l == r)
	{
		scanf("%d", &x[k]);
		a[l] = x[k];
		return;
	}
	int mid = (l + r) >> 1;
	build(k << 1, l, mid);
	build(k << 1 | 1 , mid + 1, r);
	x[k] = gcd(x[k << 1], x[k << 1 | 1]);
}

int query(int k, int l, int r, int ll, int rr)
{
	if (ll <= l && r <= rr) return x[k];
	int mid = (l + r) >> 1;
	if (rr <= mid) return query(k << 1, l, mid, ll, rr);
	else if (ll > mid) return query(k << 1 | 1, mid + 1, r, ll, rr);
	return gcd(query(k << 1, l, mid, ll, rr), query(k << 1 | 1, mid + 1, r, ll, rr));
}

void Push(int k, int l, int r)
{
	int mid = (l + r) >> 1;
	sum[k << 1] += (lazy[k] * (mid - l + 1));
	sum[k << 1 | 1] += (lazy[k] * (r - mid));
	lazy[k << 1] += lazy[k], lazy[k << 1 | 1] += lazy[k];
	lazy[k] = 0;
}

void update(int k, int l, int r, int ll, int rr, LL val)
{
	if (ll <= l && r <= rr)
	{
		sum[k] += (val * (r - l + 1));
		lazy[k] += val;
		return;
	}
	if (lazy[k]) Push(k, l, r);
	int mid = (l + r) >> 1;
	if (ll <= mid) update(k << 1, l, mid, ll, rr, val);
	if (rr > mid) update(k << 1 | 1, mid + 1, r, ll, rr, val);
	sum[k] = sum[k << 1] + sum[k << 1 | 1];
}

LL get(int k, int l, int r, int ll, int rr)
{
	if (ll <= l && r <= rr) return sum[k];
	if (lazy[k]) Push(k, l, r);
	int mid = (l + r) / 2;
	LL ans = 0;
	if (ll <= mid) ans += get(k << 1, l, mid, ll, rr);
	if (rr > mid) ans += get(k << 1 | 1, mid + 1, r, ll, rr);
	return ans;
}

bool cmp(const node &a, const node &b)
{
	return a.id < b.id;
}

int main()
{
	int t, cas = 0;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		build(1, 1, n);
		scanf("%d", &m);
		printf("Case #%d:\n", ++cas);
		for (int i = 0; i < m; i++)
		{
			scanf("%d %d", &b[i].l, &b[i].r);
			b[i].g = query(1, 1, n, b[i].l, b[i].r);
			b[i].id = i;
		}
		int cnt = 0;
		stack<pair<int, int> >s1, s2;
		for (int i = 1; i <= n; i++)
		{
			s1.push(make_pair(a[i], 1));
			while (!s1.empty()) s2.push(make_pair(gcd(s1.top().first, a[i]), s1.top().second)), s1.pop();
			while (!s2.empty())
			{
				pair<int, int>pre = s2.top(); s2.pop();
				if (!s1.empty() && pre.first == s1.top().first) pre.second += s1.top().second, s1.pop();
				s1.push(pre);
			}
			int tmp = i;
			while (!s1.empty())
			{
				pair<int, int>pre = s1.top(); s1.pop();
				c[cnt] = { tmp - pre.second + 1, tmp, i, pre.first };
				cnt++;
				tmp = tmp - pre.second;
				s2.push(pre);
			}
			while (!s2.empty())
			{
				s1.push(s2.top());
				s2.pop();
			}
		}
		sort(b, b + m);
		sort(c, c + cnt);
		for (int i = 0, j = 0; i < cnt; i = j + 1, j = i)
		{
			while (j < cnt - 1 && c[j].g == c[j + 1].g) j++;
			int l = 0, r = m - 1, pos1 = -1, pos2 = -1;
			while (l <= r)
			{
				int mid = (l + r) / 2;
				if (b[mid].g < c[i].g) l = mid + 1;
				else if (b[mid].g > c[i].g) r = mid - 1;
				else { pos1 = mid; r = mid - 1; }
			}
			if (pos1 == -1) continue;
			l = 0, r = m - 1;
			while (l <= r)
			{
				int mid = (l + r) / 2;
				if (b[mid].g < c[i].g) l = mid + 1;
				else if (b[mid].g > c[i].g) r = mid - 1;
				else { pos2 = mid; l = mid + 1; }
			}
			int tmp = i;
			for (int k = pos1; k <= pos2; k++)
			{
				while (tmp <= j && c[tmp].r <= b[k].r)
				{
					update(1, 1, n, c[tmp].l1, c[tmp].l2, 1LL);
					tmp++;
				}
				b[k].ans = get(1, 1, n, b[k].l, b[k].r);
			}
			for (int k = i; k < tmp; k++)
				update(1, 1, n, c[k].l1, c[k].l2, -1LL);
		}
		sort(b, b + m, cmp);
		for (int i = 0; i < m; i++)
			printf("%d %lld\n", b[i].g, b[i].ans);
	}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值