[JZOJ5449] Pacifist

来源:互联网 发布:趣头条刷阅读量软件 编辑:程序博客网 时间:2024/06/05 03:30

题目描述

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(n1)/2d来解出a1,因为S的值我们可以直接用读入的数据算出,那么可以算出a1,而m又是质数,解在模意义下唯一。这样求出之后,我们可以直接看看S2,S3的特征值符不符合,即看看用公式和直接用读入数据算出来结果一不一样。如果一样直接输出,正确率很高。

一个比较靠谱的方法是利用等差数列的性质。我们随便找一个数,再枚举序列的每一个数,我们知道2ai=aix+ai+x,那么如果不是ix<1i+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");}
原创粉丝点击