两数a,b同余m,显然(a-b)%m=0.....若有m%p=0...那么(a-b)%p=0....
将所有的(a[j]-a[i])记录下来...用c[1000000]的表来记...如果一个数p是N对数的同余..那么c[p]=N...
有了上面的处理,就可以方便的统计出对于当前枚举的md...会产生多少对同余的(将md所有的整数倍c[ ] 加起来)...
但产生了N对同余的..并不代表要去除N-1个数才行...比如...
3,6,9,10....(3,6),(3,9),(3,12),(6,9),(6,12),(9,12)...如果当前测试的md=3..那么会有6对同余3的....而实际上最少减去3个数可以保证不出现同余3的情况...
我做到这里就没想法了...
看了下别人的代码.. 自己理解了下..很有道理...不过也感觉很大胆...
其实并不是用md求同余对来计算最少要减去几个数..而是用其剪枝..然后再暴力查找(纯暴力..一个个试..看有多少重复..重复个数是否不大于k)
剪枝的方式是当前md的同余对>k*(k+1)/2..就找下一个md....
为什么同余对>k*(k+1)/2时就不必要用暴力的方式来判断了?
考虑一个极端的情况..给的n个数每一个都是md的整数倍..那么显然就有n*(n-1)/2个关于md的同余对...但是只需要去掉n-1个数就可以使得没有同余对了...
把n-1看成题目要求的k...那么极端情况不就是md的同余对为(k+1)*k/2吗?当md的同余对>(k+1)*k/2,无论如何也要删去k+1个数才能使得没有对于md的同余对出现...
这个剪枝相当劲爆..虽然每个通过了同余对<=(k+1)*k/2验证的md都要进行一次暴力查找判断...但时间完全ok...
Program:
#include<iostream> #include<stdio.h> #include<cmath> #include<string.h> #include<algorithm> #include<queue> #include<stack> #define ll long long #define oo 1000000000 using namespace std; int a[5005],c[1000005],n; bool used[1000005]; int main() { int i,j,k,m; while (~scanf("%d%d",&n,&k)) { for (i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); memset(c,0,sizeof(c)); for (i=1;i<n;i++) for (j=i;j<=n;j++) c[a[j]-a[i]]++; for (i=1;i<=a[n]+1;i++) { m=0; for (j=i;j<=1000000;j+=i) { m+=c[j]; if (m>k*(k+1)/2) goto A; } memset(used,false,sizeof(used)); m=0; for (j=1;j<=n;j++) { if (used[a[j]%i]) m++; used[a[j]%i]=true; if (m>k) goto A; } break; A: ; } printf("%d\n",i); } return 0; }