题目描述
papyrus 喜欢谜题… 来解一道如何?
在你面前有一个被加密了的数组,其原数组是一个等差序列,你面前的则是将原数组中的所有数字都对m 取模再打乱后而得到的新数组
papyrus 给你出的谜题就是还原出原等差序列
因为papyrus 喜欢质数,所以他给你出的谜题中的m 一定是质数
对于10% 的数据满足2<= m <= 5
对于另10% 的数据满足n = m
对于另10% 的数据满足n = m - 1
对于100% 的数据满足2 <= m <= 10^9 +7; 2 <= n <= 10^5,m 是质数,给出的序列中有可能有相同元素。
分析
这道题方法就很多了,思维比较发散。
先讲不太稳的算法。随便选定一个数a_i,它跟另外每一个数的差,一定有一个是公差。那么我们可以枚举公差,然后再来判断是否合法。
随机超时水法的判断方式是:二分确定首项是什么,即二分看看减多少个公差回到首项。因为如果公差合法,那么ai往前连续一段肯定都出现在数组中,而且m是质数,不会有鬼畜情况。唯一的鬼畜情况是2n>=m,二分的上界太大,有可能跳到序列的末尾,这时候要调整二分上界,这里可能花费很多时间。这之后,我们随机判断等差数列每一项是否出现,实现我用的是把所有系数random shuffle一下。这样我们期望复杂度大概是
O(n2/2)
,若我们确定的数记为a1,对a也shuffle,那么期望在
an/2
的地方找到合法公差。
另一个类似哈希的特征值判断方法是利用等差数列的一、二、三次求和的公式进行判断。我们枚举公差,然后用
Sn=a1n+n(n−1)/2∗d
来解出
a1
,因为S的值我们可以直接用读入的数据算出,那么可以算出
a1
,而m又是质数,解在模意义下唯一。这样求出之后,我们可以直接看看
S2,S3
的特征值符不符合,即看看用公式和直接用读入数据算出来结果一不一样。如果一样直接输出,正确率很高。
一个比较靠谱的方法是利用等差数列的性质。我们随便找一个数,再枚举序列的每一个数,我们知道 2ai=ai−x+ai+x ,那么如果不是 i−x<1或者i+x>n ,一个数一定能以我们找的那个数为对称轴,有一个跟他对称的数。那么我们不断像这样把序列匹配的删除,删除完之后假设删了cnt个,我们可以确定出这个数的位置,当然有两种可能,cnt+1或者n-cnt。那么我们再在剩下的数再确定某一个数是cnt’+1或者n-cnt’。这样四种情况肯定有合法的,直接判断一下即可。然而鬼畜的2n>=m还是要考虑,这个比较简单,只要把出现的剩余系的补集找出来,对补集做一样算法,这里面肯定2n
代码
给出第一个水法代码…
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=1e5+5,mo=60000011;
int D,l,r,mid,tmp,a[N],b[N],v[mo+5],i,j,n,m,pp,k,st,kan;
int hash(int x)
{
k=x%mo;
while (v[k]&&v[k]!=x+1) k=(k+1)%mo;
return k;
}
int main()
{
freopen("t1.in","r",stdin);
// freopen("t1.out","w",stdout);
scanf("%d %d",&m,&n);
if (m==n)
{
printf("0 1\n");
return 0;
}
fo(i,1,n) scanf("%d",a+i);
if (n==1)
{
printf("%d 0\n",a[1]);
return 0;
}
fo(i,1,n-1) b[i]=i;
srand(m);
random_shuffle(b+1,b+n);
sort(a+1,a+1+n);
fo(i,1,n)
v[hash(a[i])]=a[i]+1;
fo(i,2,n)
{
D=a[i]-a[1];
if (D<0) D+=m;
if (D>m/2) D=m-D;
if (!D) continue;
l=0;
st=a[1];
while (l<n-1&&v[hash((st-D+m)%m)])
{
r=min(n-1,m-n+l);
while (l<r)
{
mid=(l+r+1)/2;
tmp=((a[1]-1ll*mid*D)%m+m)%m;
if (v[hash(tmp)]) l=mid;
else r=mid-1;
}
st=((a[1]-1ll*l*D)%m+m)%m;
}
//st=((a[1]-1ll*l*D)%m+m)%m;
pp=1;
fo(j,1,n-1)
{
kan++;
tmp=(st+1ll*b[j]*D)%m;
if (!v[hash(tmp)])
{
pp=0;
break;
}
}
if (pp)
{
printf("%d %d\n",st,D);
return 0;
}
}
printf("-1\n");
}