牛客多校第一次反思(H题)

先记录H题,其他待填坑

本质该题等价于给出n个不同的数,O(nlogn)地求出两两之差(取绝对值)。
则可以用fft卷积。
我们记第一个多项式为
\(a_0\) + \(a_1\)\(x\) + \(a_2\)\(x^2\) + ... + \(a_n\)\(x^n\)

其中,如果i在原序列出现过,则\(a_i\) = 1,否则\(a_i\) = 0

我们记第二个多项式为
\(b_0\) + \(b_1\)\(x^{-1}\) + \(b_2\)\(x^{-2}\) + ... + \(b_n\)\(x^{-n}\)

其中,如果i在原序列出现过,则\(b^{-i}\) = 1,否则\(b^{-i}\) = 0

我们发现,如果n, m出现在原序列中(假设n > m)
则上述两个多项式的卷积中 \(x^{n - m}\)的系数必定为1。故如果某个数不是原序列的差,则卷出来对应指数的系数必定为0
而指数不能为负数,则我们给整体加个偏移量(一般为数的上限,这里我们设为500000)
在统计答案i时,取abs(i - 500000)就好。
AC代码:

#include <iostream>
#include <complex>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 2e6 + 10;

//这里为fft板子,一般来说MAXN根据题目而定
const double Pi = acos(-1.0);
typedef complex<double> CP;
CP a[MAXN], b[MAXN];
int limit = 1;
void FFT(CP *x, int inv) {
   int bit = 1, m;
   CP stand, now, temp;
   while((1 << bit) < limit) bit++;
   for (int i = 0; i < limit; i++) {
       m = 0;
       for (int j = 0; j < bit; j++)
           if(i & (1 << j)) m |= (1 << (bit - j - 1));
       if(i < m) swap(x[m], x[i]);
   }
   for (int len = 2; len <= limit; len <<= 1) {
       m = len >> 1;
       stand = CP(cos(2 * Pi / len), inv * sin(2 * Pi / len));
       for (CP *p = x; p != x + limit; p += len) {
           now = CP(1, 0);
           for (int i = 0; i < m; i++, now *= stand) {
               temp = now * p[i + m];
               p[i + m] = p[i] - temp;
               p[i] = p[i] + temp;
           }
       }
   }
   if(inv == -1) 
       for (int i = 0; i < limit; i++)
           x[i].real(x[i].real() / limit);
}
//板子结尾


int n;
int tmp;
int vis[MAXN];
int maxn = -1;
int flag;
int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++) {
	cin >> tmp;
	maxn = max(maxn, tmp);
	a[tmp].real(1);
        b[500000 - tmp].real(1);    //负数的偏移量
    }
    while (limit <= n + 500000) limit <<= 1;
    FFT(a, 1);
    FFT(b, 1);
    for (int i = 0; i < limit; i++) a[i] = a[i] * b[i];
    FFT(a, -1);
    for (int i = 0; i <= limit; i++) {
    	if ((int)float(a[i].real() + 0.5) > 0) {
    		 vis[abs(i - 500000)] = 1;   //统计出现过时要减去偏移量取绝对值
    	}
    }
    for (int i = n; i <= maxn + 1; i++) {
    	flag = 1;
    	for (int j = i; j <= 500000; j += i) {
	    if (vis[j]) {
	    flag = 0;
	    break;
	    }
	} 
	if (flag) {
		cout << i << "\n";
		return 0;
	}
    }
    return 0;
}
posted @ 2021-07-18 21:39  Leins  阅读(34)  评论(0编辑  收藏  举报