题意:
给出一个环,环上有n(<=2000)个数字(<=5e7),然后将这个环分成1~n个连续序列,各个序列和之间的最大公因数。
题解:
我一开始想到的是二分,然后对于二分就会想怎么check,那么可以枚举这n个数和的因数,因为答案一定在这里面,然后就会找出对于每个因数可以分成的段数,但是这个并不满足单调性QAQ,但提供了一定的思路
考虑从大到小枚举因数,然后对于这个因数分成尽量多的部分,因为不难知道如果a % x == 0,(a + b) % x == 0 那么b % x == 0,因为这样就可以一直贪心找最大的因数。
然后现在的问题就是考虑环的情况,一般的做法是断环为链,枚举起点到终点找出有多少段%x == 0,但是可以稍微变一下形,如果pre[i] % x == y, pre[j] % x == y,那么i ~ j这一段%x为0,那么(1 ~ i) + (j+1 ~ n) % x == 0,那么现在的问题就是统计余数出现最多的次数,就完美处理了环的情况。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 4e3 + 7;
#define LL long long
vector <LL> num;
int n;
LL pre[N];
int main () {
scanf ("%d", &n);
for (int i = 1; i <= n; ++i) {
int x;
scanf ("%d", &x);
pre[i] = pre[i-1] + x;
}
for (LL i = 1; i * i <= pre[n]; ++i) {
if (pre[n] % i == 0) {
if (i * i == pre[n]) {
num.push_back(i);
continue;
}
num.push_back(i);
num.push_back(pre[n] / i);
}
}
sort (num.begin(), num.end());
int K = 1;
for (int i = num.size() - 1; i >= 0; --i) {
int maxi = 0;
vector <LL> v;
for (int j = 1; j <= n; ++j) v.push_back(pre[j] % num[i]);
sort (v.begin(), v.end());
int pre = 0;
for (int j = 0; j < v.size(); ++j) {
if (v[j] == v[j+1] && j != v.size()-1) continue;
maxi = max (maxi, j - pre + 1);
pre = j + 1;
}
while (K <= maxi) {
printf ("%lld\n", num[i]);
++K;
}
}
return 0;
}
总结:
这种求因数的题,并且数的范围不大,可以直接暴力枚举因数。。。。。。还有注意的是公式的变换~