题意:
将围成一圈的 n 个数划分为 k (1 <= k <= n) 段,对每段求和,再求这些和的最大公约数,要求最大公约数最大。
思路及详细分析见 http://blog.csdn.net/u010885899/article/details/49584299 (感谢原Po)
几个要点:
1. 每一段和的最大公约数 必然是 整体和 的最大公约数。
因为 d | a && d | b => d | (a + b)
2. 对于一个数 d,判断是否可以作为 k 段的公约数,只需要将 k 个和求个前缀和,判断有没有超过 k 个前缀和 mod d 同余。
这一点也很好理解,对于任意的 i, j (i < j), pre[i] ≡ pre[j] (mod d) => d | (pre[j] - pre[i]) = sum(i + 1, j), 将 第 i + 1 个数到 第 j 个数划为一段,则 d 整除该段的和;
对于这一串同余的数当中的第一个和最后一个,因为 d | sum1, d | sum2, ..., d | sumn, d | sum, 所以 d | (sum - sum1 - sum2 - ... - sumn),即将从最后一个同余的数往后一个位置开始,向后,再转到前面来(因为是环),一直到第一个同余的数的这些数化为一段,d 也整除该段的和
3. 从大向小枚举 d,有 k 个前缀和 mod d 同余 => 可以划分为 1~k 段。
显然,从上一次的 kmax + 1 开始划分,便保证了对于每一个划分段数 k,和都是最大的,即最大公约数。
另外,因为数据范围的问题,1<=n<=2000,1<=ai<=5e7, mod 下来的数用数组是存不下的,所以要用 map
AC代码如下:
#include <bits/stdc++.h>
#define maxn 2010
typedef long long LL;
LL pre[maxn], fac[1000010], ans[maxn], a[maxn];
using namespace std;
map<LL, LL> cnt;
bool cmp(LL a, LL b) { return a > b; }
inline LL max(LL a, LL b) { return a > b ? a : b; }
int main() {
int n;
scanf("%d", &n);
scanf("%lld", &a[0]); pre[0] = a[0];
for (int i = 1; i < n; ++i) {
scanf("%lld", &a[i]);
pre[i] = pre[i - 1] + a[i];
}
int tot = 0;
LL sum = pre[n - 1];
for (LL i = 1; i * i <= sum; ++i) {
if (i * i == sum) { fac[tot++] = i; break; }
if (sum % i == 0) fac[tot++] = i, fac[tot++] = sum / i;
}
sort(fac, fac + tot, cmp);
int k = 1;
for (int i = 0; i < tot; ++i) {
cnt.clear();
int maxx = 0;
for (int j = 0; j < n; ++j) {
int p = pre[j] % fac[i];
if (cnt.find(p) != cnt.end()) ++cnt[p];
else cnt.insert(make_pair(p, 1));
maxx = max(maxx, cnt[p]);
// printf("cnt : %d\n", cnt[p]);
}
while (!ans[k] && k <= maxx) ans[k++] = fac[i];
if (k == n + 1) break;
}
for (int i = 1; i <= n; ++i) printf("%lld\n", ans[i]);
return 0;
}
题意:
给定一串数 a[i], 要找 k 和 m,使得超过一半的数满足 a[i] % m == k, (m > 1, 0 <=k < m, n <= 1e5, a[i] <= 1e9)
想法是:相邻两个数做个差,得到 n - 1 个数,要使其中多于一半的数的最大公约数 > 1
(完蛋,这该怎么做;完蛋,大家怎么都会做;完蛋,这签到题啥情况)
后来学姐指点迷津,直接看奇数偶数就行了...
取 m = 2, 奇数多 k = 1, 偶数多 k = 0.
(简直想一头撞死)
AC代码如下:
#include <bits/stdc++.h>
void work() {
int n;
scanf("%d", &n);
int tot = 0;
for (int i = 0; i < n; ++i) {
int x;
scanf("%d", &x);
if (x & 1) ++tot;
}
int m = 2, k;
int even = n - tot;
if (even >= tot) k = 0;
else k = 1;
printf("%d %d\n", m, k);
}
int main() {
int T;
scanf("%d", &T);
while (T--) work();
return 0;
}