hihocoder 1246 王胖浩与环
题意:
给出一个环,环上有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; }
总结:
这种求因数的题,并且数的范围不大,可以直接暴力枚举因数。。。。。。还有注意的是公式的变换~