[JZOJ5449] Pacifist

xiaoxiao2021-02-28  13

题目描述

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"); }
转载请注明原文地址: https://www.6miu.com/read-1100204.html

最新回复(0)