题意:
给出序列a,质数m,序列长n,序列a是对m取模后的序列,问是否能找到一个原序列是等差数列,输出首项和公比。否则输出-1。
解题思路:
首先需要知道两个公式:
1.等差数列各项和公式变形:a1=(Sn-n*(n-1)/2*d)/n
2.等差数列各项的平方的和的公式:Sn^2=n(a1)^2+n(n-1)(2n-1)d^2/6+n(n-1)*d*a1(以前不知道这公式。。)
先讲a序列从小到达排列,我们去枚举a[i]-a[0],因为a[i]中肯定有与a[0]相邻的点,所以我们一定能求出一个d,这个我已开始对一个特殊情况有疑问,问了q神后才知道,如果求不出d来,那么可以求出一个-d然后再加上就m就是合法的。
我想的特殊情况是a序列是2,4,6,8,对m为7取模后为1,2,4,6,这样的就求不出2这个公差了,但是问了q神之后知道,这样求出公差-2,对7取模后最小的数是8,因为和8相邻的点只有6了,那么就求出一个reverse过的公差-2(即6-8)的序列,在这里-2+m就是5,也就是6-1得到的数据。orz,模q
我们枚举出q后,可以根据公式1求出首项a1,这里由于除数有未知量,所以取模要求逆元,用费马小定理求一下就可以,然后再用公式2去验证是否满足来判断,d和a1是否正确。在这基础上还要确定下求出来的序列是否和a序列相同,相同就是符合的解了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn];
int b[maxn];
int fp(int a, int n,int m)
{
int res=1;
int tem=a;
while(n)
{
if(n&1)res=1LL*res*tem%m;
tem=1LL*tem*tem%m;
n>>=1;
}
return res;
}
int main()
{
int n, m;
cin>>m>>n;
int i;
int s[2];
s[0]=s[1]=0;
for(i=0; i<n; i++)
{
scanf("%d", &a[i]);
s[0]=(a[i]+s[0])%m;
s[1]=(s[1]+1LL*a[i]*a[i]%m)%m;
}
if(n==1)return 0*printf("%d 0\n", a[0]);
if(n==m)return 0*printf("0 1");
sort(a, a+n);
for(int i=1; i<n; i++)
{
int d=(a[i]-a[0]);
int x=(s[0]-1LL*n*(n-1)/2%m*d%m+m)*fp(n,m-2,m)%m;
int tem=1LL*n*x%m*x%m;
tem=(tem+1LL*n*(n-1)%m*d%m*x%m)%m;
tem=(tem+1LL*n*(n-1)*(2*n-1)/6%m*d%m*d%m)%m;
int tmp=1LL*n*x%m*x%m;
tmp=(tmp+1LL*n*(n-1)%m*d%m*x%m)%m;
tmp=(tmp+1LL*n*(n-1)*(2*n-1)/6%m*d%m*d%m)%m;
if(s[1]==tem)
{
b[0]=x;
for(int i=1; i<n; i++)
{
b[i]=(b[i-1]+d)%m;
}
sort(b, b+n);
// for(int i=0; i<n; i++)printf("%d ", b[i]);
// printf("\n");
bool isok=true;
for(int i=0; i<n; i++)isok&=(a[i]==b[i]);
if(isok)return 0*printf("%d %d\n", x, d);
}
}
printf("-1\n");
return 0;
}