D. Integers Have Friends(线段树 + 双指针)

Problem - D - Codeforces

 英国数学家约翰·利特伍德(John Littlewood)曾这样评价印度数学家斯里尼瓦萨·拉马努金(Srinivasa Ramanujan):“每个正整数都是他的一个朋友。”事实证明,正整数也可以成为彼此的朋友!给定一个数组a,由不同的正整数组成。定义子数组ai, ai+1,aj为好友组当且仅当存在整数m 22使得a;Mod m= ai+1 Mod m=.=a;对m取余,其中x对y取余表示z除以y时的余数。你的朋友格雷戈想知道最大的朋友群有多大。输入每个测试包含多个测试用例。第一行包含测试用例的数量t (1 < t < 2 - 104)。每个测试用例都以包含整数n (1 <n≤2- 105)的一行开始,即数组a的大小。下一行包含n个正整数a1, a2,.., an (1 <ai <1018 a中的所有数字都是不同的。),表示数组a的内容它保证所有测试用例的n和小于2- 105。输出输出应该由t行组成。每一行都应该由一个整数组成,即a中最大的朋友组的大小。

Example

input

Copy

4
5
1 5 2 4 6
4
8 2 5 10
2
1000 2000
8
465 55 3 54 234 12 45 78

output

Copy

3
3
2
6

请注意在第一个测试用例中,数组是[1,5,2,4,6]。最大的朋友群是[2,4,6],因为所有这些数字都对2模等于0,所以m = 2。在第二个测试用例中,数组是[8,2,5,10]。最大的朋友群是[8,2,5],因为所有这些数字都等于2对3取模,所以m = 3。在第三种情况下,最大的朋友群是[1000,2000]。显然有很多可能的m值。

题解:
我们通过观察,可以发现如果相邻数的差值gcd如果不为1,他们%m的值是一样的

例如

1 4 10

数组差值为3 6 gcd为3,数组%3的值都为1

至于怎么证明的,我也不是很清楚

那么题意就很明白了,我们首先处理出来数组间n-1个差值,找到最长一段gcd不为1的子数组即可

我的错误代码

#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
ll a[200050];
ll b[200050];
void solve()
{
	int n;
	scanf("%d",&n);
	for(int i = 1;i <= n;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(int i = 1;i < n;i++)
	{
		b[i] = abs(a[i+1] - a[i]);
	}
	int ans = 1;
	n--;
	int l = 1,r = 1;
//	for(int i = 1;i <= n;i++)
//	{
//		cout << b[i] <<" ";
//	}
//	cout <<"\n";
	while(l <= n&&r <= n)
	{
		ll g = 0;
		while(r <= n)
		{
			g = __gcd(b[r],g);
//			cout <<l <<" "<<r<<" "<<g<<"\n";
			if(g == 1)
			{
				ans = max(ans,r - l + 1);
				if(b[r] != 1)
				l = r;
				else
				{
					l = r + 1;
					r++;
				}
				break;
			}
			else
			{
				r++;
			}
		}
		if(r > n)
		{
			ans = max(ans,n - l + 2);
		}
	}
	printf("%d\n",ans);
	
}
//1
//20
//16 15 17 8 30 23 20 28 27 6 1 18 24 2 10 5 14 29 12 7
signed main() 
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
	int t = 1;
    scanf("%d",&t);
	while (t--) 
	{
		solve();
	}
	return 0;
}

原因

按我的思路,一段找完,直接跳到下一段了,没有考虑中间重合的情况

但是每次枚举左右边界,时间复杂度又太高,

所以我们用线段树,来维护区间gcd的值,每次枚举右区间

左区间每次加到l ~ r不为1,就break

AC代码

#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
ll a[200050];
ll b[200050];
struct node
{
	ll l,r,gcd;
}tre[800050];
void pushup(int rt)
{
	tre[rt].gcd = __gcd(tre[rt*2].gcd,tre[rt*2 + 1].gcd);
}
void build(int rt,int l,int r)
{
	tre[rt].l = l;
	tre[rt].r = r;
	if(l == r)
	{
		tre[rt].gcd = b[l];
		return ;
	}
	int mid = (l + r)/2;
	build(rt*2,l,mid);
	build(rt*2 + 1,mid + 1,r);
	pushup(rt);
}
ll ask(int rt,int l,int r)
{
	if(tre[rt].l >= l&&tre[rt].r <= r)
	{
		return tre[rt].gcd;
	}
	int mid = (tre[rt].l + tre[rt].r) /2;
	ll ans = 0;
	if(l <= mid)
	{
		ans = ask(rt*2,l,r);
	}
	if(r > mid)
	{
		ans = __gcd(ans,ask(rt* 2 + 1,l,r));
	}
	return ans;
}
void solve()
{
	int n;
	scanf("%d",&n);
	for(int i = 1;i <= n;i++)
	{
		scanf("%lld",&a[i]);
	}	
	if(n == 1)
	{
		printf("1\n");
		return ;
	}
	n--;
	for(int i = 1;i <= n;i++)
	{
		b[i] = abs(a[i + 1] - a[i]);
	}
	build(1,1,n);
	int l = 1;
	int ans = 1;
	for(int r = 1;r <= n;r++)
	{
		if(b[r] == 1)
		continue;
		while(l < r)
		{
			if(ask(1,l,r) == 1)
			{
				l++;
			}
			else
			break;
		}
		ans = max(ans,r - l + 2);
	}
	printf("%d\n",ans);
	for(int i = 1;i <= n*4;i++)
	{
		tre[i] = {0,0,0};
	}
}
//1
//20
//16 15 17 8 30 23 20 28 27 6 1 18 24 2 10 5 14 29 12 7
signed main() 
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
	int t = 1;
    scanf("%d",&t);
	while (t--) 
	{
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值