英国数学家约翰·利特伍德(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;
}