题目
正解
比赛时几乎没有怎么思路。
其实这个东西应该很显然:如果要让答案大于$0¥,那么至少存在两个会有冲突,也就是说在模了之后至少有两个不同的数的位置会重叠。
所以只需要枚举两两之间的差的约数,都试一遍就可以了。
这样当然会TLE。
考虑某个数\(p\)以及它的倍数\(kp\),显然,在\(kp\)中撞上的在\(p\)中一定也能撞上,所以如果\(p\)符合条件,就不用算\(kp\)了。
所以,对于某个数\(x\),求出它的最小质因子\(y\),只有\(\frac{x}{y}
按照这个性质,可以进一步推出:要么\(x\)是素数,否则\(x\in [n,n^2]\)
对于\([n,n^2]\)的每个都枚举一遍,借助并查集用接近\(O(n)\)的时间计算。
对于大质数,用Polllard-Rho方法分解出来之后计算,计算的时候与前面的类似,不过还要打个哈希表来模拟下标。
代码
其实这题说起来容易,但代码实现起来真的有点恶心。
话说Pollard-Rho有个不错的优化,就是不要每次枚举都计算\(gcd\),乘\(128\)个之后再求\(gcd\)。
为了防止圈过小的情况,还要倍增一下。
然后就是我终于知道了原来long long
相乘取模借助long double
可以做到几乎没有误差。最重要的是要在相乘之前先让它们对模数取模。
还有,经过实测,x*x+c
的伪随机方式似乎带着一种奇怪的魔力。用其它的方法进行伪随机时间会T飞。
using namespace std;
#include
#include
#include
#include
#include
#include
#define N 210
#define ll long long
#define ull unsigned long long
int n;
long long a[N];
int lst[N];
ull ans;
ll mul(ll x, ll y, ll mo) {
ll z = (long double) x * y / mo;
z = x * y - z * mo;
if(z < 0) z += mo; else if(z > mo) z -= mo;
return z;
}
inline ull qpow(ull x,ull y,ull mo){
ull res=1;
for (;y;y>>=1,x=mul(x,x,mo))
if (y&1)
res=mul(res,x,mo);
return res;
}
const int p[9]={2,3,5,7,11,13,17,19,23};
inline bool mr(ull v){
if (v==0 || v==1)
return 0;
for (int i=0;i<9;++i){
if (v==p[i])
return 1;
if (v%p[i]==0)
return 0;
}
ull x=v-1,k=0;
while (!(x&1))
x>>=1,++k;
for (int i=0;i<9;++i){
ull t=qpow(p[i],x,v);
if (t==1 || t==v-1)
continue;
for (int j=1;j=x?fa[x]-x:len+fa[x]-x);
x=fa[x];
int y=getfa((x+1)%len);
fa[x]=y;
}
return res;
}
#define mo 3001
ull id[mo],used[N][2];
inline int myhash(ull key){
int i=key%mo;
for (;id[i] && id[i]!=key;i=(i+1==mo?0:i+1));
return i;
}
inline int newnode(ull key){
int i=myhash(key);
if (id[i]==0){
id[i]=key;
fa[i]=i;
}
return i;
}
inline ull calc2(ull len){
ull res=0;
for (int i=1;i<=n;++i){
if (lst[i]){
res+=need[i]=need[lst[i]];
continue;
}
ull x=a[i]%len,ha=myhash(x);
if (id[ha]==0){
id[ha]=x;
fa[ha]=newnode((x+1)%len);
used[i][0]=ha;
used[i][1]=fa[ha];
need[i]=0;
continue;
}
getfa(ha);
res+=need[i]=(id[fa[ha]]>=x?id[fa[ha]]-x:len+id[fa[ha]]-x);
ha=fa[ha];
x=id[ha];
int y=getfa(newnode((x+1)%len));
fa[ha]=y;
used[i][0]=ha;
used[i][1]=y;
}
for (int i=1;i<=n;++i)
if (!lst[i])
id[used[i][0]]=id[used[i][1]]=0;
return res;
}
set vis;
void find(ull v){
if (v<=n*n)
return;
if (vis.find(v)!=vis.end())
return;
vis.insert(v);
if (mr(v)){
ans=max(ans,calc2(v));
return;
}
ull d=rho(v);
ull u=v/d,g=gcd(d,u);
if (g!=1){
d/=g,u/=g;
find(g);
}
find(d),find(u);
}
int main(){
freopen("hash.in","r",stdin);
freopen("hash.out","w",stdout);
srand(time(0));
int type;
scanf("%d%d",&type,&n);
for (int i=1;i<=n;++i){
scanf("%lld",&a[i]);
for (int j=i-1;j>=0;--j)
if (a[i]==a[j]){
lst[i]=j;
break;
}
}
for (int i=n;i<=n*n;++i)
ans=max(ans,calc1(i));
for (int i=0;i<9;++i)
if (p[i]>n*n)
ans=max(ans,calc1(p[i]));
for (int i=1;i<=n;++i)
for (int j=i+1;j<=n;++j){
ull tmp=abs(a[i]-a[j]);
if (tmp==0)
continue;
for (int k=0;k<9;++k)
while (tmp%p[k]==0)
tmp/=p[k];
find(tmp);
}
printf("%lld\n",ans);
return 0;
}
总结
真是一个恶心的程序……
不过,这也让我了解怎么更加优美地打Pollard-Rho。