codeforces 1547F

题意

链接: 题目.

给定一个长度为n的数组,下标从1到n,其中an和a1相连
每轮操作得到一个新的数组b,对于所有的i∈[1,n],b[i]=gcd(a[i],a[(i+1)%n]).最后将数组b复制给a
问,执行上述操作多少轮之后,a数组中的所有数字都相同

思路

最终的结果有多种可能
两个数取gcd,相当于保留两个数的公共因子,对于整个序列执行无数次gcd操作之后,等价于序列中每个数变成原序列中所有公共因子的乘积,我们最终的结果可能是1到gcd(a[1],a[n])之间的因子,我们不妨执行a[i]/=gcd(a[1],a[n]),这样结果就是唯一的1,
现在的目的就是执行多少次操作之后数组全部变成一,
对于每一个位置的数是与下一位置的数进行gcd操作,如果两个数字本身互质,则操作一次就能得到1,
如果不互质,假设存在公因子2,组此轮变化后,a[i]=2,a[i+1]根据a[i+2]来决定,a[i+2]由,,,,,
当a[i+1]操作一次之后不含有因子2,则表明a[i+2]也不含因子2,因此两轮之后就可以将a[i]变成1
当a[i+1]操作一次之后存在因子2,则表明a[i+2]也存在因子2,此时我们假设a[i+2]的情况
我们观察中得出,如果a[i], a[i + 1], …, a[i + k]都存在公因子2, a[k + 1]不存在因子2,我们需要进行k轮操作之后,才能将a[i]变成1
对于本位置,我们把所有的位置取max。
线段树+树外二分:
我们用线段树维护每个区间静态gcd的信息. 对于每个位置查询最靠左gcd = 1的位置即可.
这里还有一点点细节问题: 由于a[n]后面是a[1], 题目中的数组是个环, 我们可以通过把数组复制一遍的方式来模拟环. 即: a[n + 1] = a[1], a[n + 2] = a[2], …, a[n + n] = a[n].

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 4E5 + 10; //记得开2倍
int a[N];
struct node {
	int l, r;
	int val;
}t[N << 2];
void pushup(int x) { t[x].val = gcd(t[x << 1].val, t[x << 1 | 1].val); }
void build(int l, int r, int x = 1) {
	t[x] = { l, r, a[l] };
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
	pushup(x);
}
int ask(int l, int r, int x = 1) { //查询[l, r]的gcd
	if (l <= t[x].l and r >= t[x].r) return t[x].val;
	int mid = t[x].l + t[x].r >> 1;
	int res = 0;
	if (l <= mid) res = ask(l, r, x << 1);
	if (r > mid) res = gcd(res, ask(l, r, x << 1 | 1));
	return res;
}
int main()
{
	int t; cin >> t;
	while (t--) {
		int n; scanf("%d", &n);

		int d = 0; //得到gcd(a[]).
		rep(i, n) scanf("%d", &a[i]), d = gcd(d, a[i]);
		if (d != 1) rep(i, n) a[i] /= d;

		rep(i, n) a[n + i] = a[i];
		n <<= 1;

		build(1, n);

		int res = 0;
		rep(i, n / 2) {
			if (a[i] == 1) continue;

			int l = i + 1, r = n; //右端点的取值区间
			while (l < r) {
				int mid = l + r >> 1;
				int cou = ask(i, mid);
				if (cou == 1) r = mid;
				else l = mid + 1;
			}
			res = max(res, r - i);
		}

		printf("%d\n", res);
	}

	return 0;
}

链接: 转载.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.0-0.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值