hiho 1246 && hdu 6075(17多校四09) 同余 与 最大公约数

34 篇文章 0 订阅
12 篇文章 0 订阅

hiho 1246 题目链接


题意:

将围成一圈的 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;
}


hdu 6075 题目链接


题意:

给定一串数 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值