【代码源每日一题Div1】106.选数

题目链接

选数 - 题目 - Daimayuan Online Judge

题目描述

给定 n 个正整数 a_1,a_2,...,a_n 。 要求从其中选出若干数字, 使得这些数字的和 mod n = 0 (对于每个下标最多只能选择一次)。

输入格式

第一行一个数字 n, 表示数字个数。

接下来一行 n 个整数 a_1,a_2,...,a_n , 表示这 n 个数。

输出格式

第一行输出 M, 表示选择的数的个数。

第二行输出 M 个正整数, 用空格隔开, 表示这些数字的下标。

如果有多种方案满足要求, 输出任意一种。

如果没有满足要求的方案, 输出 −1。

样例输入

4
1 3 2 5

样例输出

2
2 4

样例解释

3 + 5 = 8, 8 mod 4 = 0。

数据规模

所有数据保证 1\leqslant n \leqslant 1000001 \leqslant a_i \leqslant 10^9

解题思路

每个数先对 n 取模,这样得到 n 个在 0 ~ n - 1 的数,对这个数组求所有的前缀和 mod n,可以发现,如果有一个前缀和是 mod n = 0 的,那么直接从第一个数到这个数全选即可;如果所有前缀和都不为 0,因为一共有 n 个前缀和,但是每个前缀和 mod n 一定是在 0 ~ n - 1 的,根据抽屉原理,一定至少有两个前缀和 mod n 的值相等。那么只要找到这样的两个前缀和的下标记为 l 和 r[l + 1, r] 的数一定满足相加 mod n = 0,否则不可能在 [1, l] 前缀和的基础上加上这些数 mod n 还能让 [1, r] 前缀和与 [1, l] 前缀和相等。这样也就证明了本题不存在无解情况。

AC代码

#include <bits/stdc++.h>
#define lowbit(x) (x & -x)
#define mid (l + r >> 1)

using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 100005;

struct node{
    int id, val;
    bool operator < (const node &u) const{
        return val < u.val;
    }
}f[N];

int main(){
    //freopen("input.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n;
    cin >> n;
    int pos = -1;
    for(int i = 1; i <= n; i++){
        cin >> f[i].val;
        f[i].val = (f[i].val + f[i - 1].val) % n;
        f[i].id = i;
        if(f[i].val == 0) pos = i;
    }
    
    if(pos != -1){
        cout << pos << endl;
        for(int i = 1; i <= pos; i++)
            cout << i << ' ';
        cout << endl;
    }
    else{
        sort(f + 1, f + n + 1);
        int l, r;
        for(int i = 1; i < n; i++){
            if(f[i].val == f[i + 1].val){
                l = min(f[i].id, f[i + 1].id);
                r = max(f[i].id, f[i + 1].id);
                break;
            }
        }
        cout << r - l << endl;
        for(int i = l + 1; i <= r; i++)
            cout << i << ' ';
        cout << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值