CF1482B Restore Modulo

知识点:

同余、(为什么不是GCD!) GCD(题解写到一半发现GCD也是正解!)
题目链接

题意

给你原不减等差数列每个元素取模后的序列,求满足题意的最大的模和公差,且公差小于模。不存在原序列输出-1,模无限大输出0。

思路

之前做过一道题:所有序列的元素取模后相等,求最大的模。
如出一辙啊,就是这道题的特殊情况(公差为零)。
于是我按照那题的思路用了半天的gcd
(题解写到此处的时候我发现有点不对劲,竟然把gcd解法调出来了!)
于是我按照那题的思路用了gcd解法,此题解为一题多解。

思路1(GCD)

在模系统中求最大模数,考虑GCD。
假设存在原序列a,则 a i + 1 − a i = a i − a i − 1 = ⋯ = d a_{i+1}-a_i=a_i-a_{i-1}=\dots=d ai+1ai=aiai1==d(d为公差),模系统中
a i + 1 − a i ≡ a i − a i − 1 ≡ ⋯ ≡ d ( m o d   m ) a_{i+1}-a_i\equiv a_i-a_{i-1}\equiv \dots \equiv d\quad(mod\ m) ai+1aiaiai1d(mod m)
从这个式子中求m仍然困难。转化为
( a i + 1 − a i ) − ( a i − a i − 1 ) ≡ 0 ( m o d   m ) (a_{i+1}-a_i)-(a_i-a_{i-1})\equiv 0\quad(mod\ m) (ai+1ai)(aiai1)0(mod m)
( a i − a i − 1 ) − ( a i − 1 − a i − 2 ) ≡ 0 ( m o d   m ) (a_{i}-a_{i-1})-(a_{i-1}-a_{i-2})\equiv 0\quad(mod\ m) (aiai1)(ai1ai2)0(mod m)
… \dots

这里稍微总结一下与0和1同余情况下的意义:
a与0同余:a整除模
a与1同余:gcd(a,m)=gcd(a%m,m)=gcd(1,m)=1,即a与模互质。

即求最大的m使得所有 ≡ \equiv 左边的式子都整除m,此正为gcd的定义!
即求 m = g c d ( ( a i + 1 − a i ) − ( a i − a i − 1 ) , ( a i − a i − 1 ) − ( a i − 1 − a i − 2 ) , …   ) m=gcd((a_{i+1}-a_i)-(a_i-a_{i-1}),(a_{i}-a_{i-1})-(a_{i-1}-a_{i-2}),\dots) m=gcd((ai+1ai)(aiai1),(aiai1)(ai1ai2),)
c可取任意 a i − a i − 1 a_i-a_{i-1} aiai1(不妨取 a 2 − a 1 a_2-a_1 a2a1)的最小正整数解。
特判1:n<3时,必无限大(请手动模拟)
特判2:存在 a i ≥ m a_i\geq m aim时,元素比模还大,无解(不明白为什么不能取m的因子)

思路2(同余)

观察一定公差小于模,所以序列上升部分后面一定是相对前面未取模的结果,易知 c = a i − a i − 1 c=a_i-a_{i-1} c=aiai1,下降部分后面一定是相对前一元素取模的结果, a i − 1 + c ≡ a i ( m o d   m ) a_{i-1}+c \equiv a_i\quad (mod\ m) ai1+cai(mod m)
同样地, a i − 1 − a i + c ≡ 0 ( m o d   m ) a_{i-1}-a_i+c \equiv 0\quad (mod\ m) ai1ai+c0(mod m),说明 ≡ \equiv 左边的式子整除m,取m为 ≡ \equiv 左边的式子即为最大值。
求出c,m后检验整个序列即可。
特判1:n<3时,必无限大(请手动模拟)
特判2:存在 a i ≥ m a_i\geq m aim时,元素比模还大,无解(不明白为什么不能取m的因子)
特判3:没有上升部分(求不出c)或没有下降部分(求不出m),可发现如果取模后是等差数列,m无限大,否则不存在原序列。

代码1(GCD)

#include<bits/stdc++.h>
#define ll long long
#define pii pair<ll,ll>
using namespace std;
const ll N=2e5+10;
const ll inf=0x3f3f3f3f3f3f3f3f;

ll n;
ll arr[N];
ll d[N];//d[i]=arr[i]-arr[i-1]

void solve()
{
   
	scanf("%lld",&n);
	for(ll i=1; i<=n; i++) scanf("%lld",&arr[i]);
	if(n<3)//特判1
	{
   
		puts("0");
		return;
	}
	for(ll i=2; i<=n; i++) d[i]=arr[i]-arr[i-1];
	ll gcdd=0;//初始化无穷大
	for(ll i=3; i<=n; i++)
	{
   
		ll val=abs(d[i]-d[i-1]);//负数整除m与正数整除m等价
		if(val==0) continue;//防止gcd(0,0)RE
		gcdd=__gcd(gcdd,val);
	}
	if(gcdd==0)//无穷大
	{
   
		puts("0");
		return;
	}
	for(ll i=1;i<=n;i++)
	{
   
		if(arr[i]>=gcdd)//判断有没有比模还大的数
		{
   
			puts("-1");
			return;
		}
	}
	printf("%lld %lld\n",gcdd,((arr[2]-arr[1])%gcdd+gcdd)%gcdd);
	//参数3为最小非负整数写法。
}

int main()
{
   
	ll t;
	scanf("%lld",&t);
	while(t--)
		solve();
	return 0;
}

代码2(流行题解)

#include<bits/stdc++.h>
#define ll long long
#define pii pair<ll,ll>
using namespace std;
const ll N=2e5+10;

ll n;
ll arr[N];

void solve()
{
   
	scanf("%lld",&n);
	for(ll i=1; i<=n; i++) scanf("%lld",&arr[i]);
	if(n<3)
	{
   
		puts("0");
		return;
	}
	//检验是否为等差数列
	ll d=arr[2]-arr[1];//公差
	bool f=false;
	for(ll i=3; i<=n; i++)
	{
   
		if(arr[i]-arr[i-1]!=d)
		{
   
			f=true;
			break;
		}
	}
	if(!f)//公差相同
	{
   
		puts("0");
		return;
	}
	ll c=-1;
	for(ll i=2; i<=n; i++)
	{
   
		if(arr[i-1]<arr[i])//找到上升部分求c
		{
   
			c=arr[i]-arr[i-1];
			break;
		}
	}
	if(c==-1)//找不到
	{
   
		puts("-1");
		return;
	}
	ll m=-1;
	for(ll i=2; i<=n; i++)
	{
   
		if(arr[i-1]>arr[i])//找到下降部分求m
		{
   
			m=arr[i-1]-arr[i]+c;
			break;
		}
	}
	if(m==-1)
	{
   
		puts("-1");
		return;
	}
	//检验是否存在比模还大的数
	for(ll i=1; i<=n; i++)
	{
   
		if(arr[i]>=m)
		{
   
			puts("-1");
			return;
		}
	}
	//检验整个序列(模拟)
	for(ll i=2; i<=n; i++)
	{
   
		if((arr[i-1]+c)%m!=arr[i])
		{
   
			puts("-1");
			return;
		}
	}
	printf("%lld %lld\n",m,c);
}

int main()
{
   
	ll t;
	scanf("%lld",&t);
	while(t--)
		solve();
	return 0;
}
全部评论

相关推荐

10-11 17:30
湖南大学 C++
我已成为0offer的糕手:羡慕
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务