lightoj-1134-Be Efficient

来源:互联网 发布:mac怎么打开pdg 编辑:程序博客网 时间:2024/05/17 04:40

题目传送门:https://vjudge.net/problem/LightOJ-1134


先思考一个问题:


前缀和L与前缀和R对m取余之后余数相等,那么区间(L,R)与m有什么关系?


例子:

输                    入:   1  2  3  4  5  6

前        缀        和:   1  3  6  10 15 21

前缀和并对3取余:   1  0  0  1  0  0    (编号为  1-3、2-3......就是横纵坐标)


1-3和1-4结果都是1,那说明在2-1到2-4之间加上的那个数一定为3的倍数,所以就一定存在一个子序列和为3的倍数。

但是

如果像3-2、3-3、3-5、3-6一样存在四个,任意两这个都存在一个子序列,所以就是4 * (4 - 1) /  2 。

再但是

如果前缀和对3取余后等于0,说明他自身也是3 的倍数。

所以ans = 1 + 4 * (4 - 1) / 2 + 4 = 11



    int t, cnt = 0;     int a[100005], s[100005];    int i, num;      cin >> t;      while(t--)      {          memset(s, 0, sizeof(s));          int n, m;          scanf("%d%d", &n, &m);           for(i = 1; i <= n; i++)          {              scanf("%d", &num);              a[i] = (a[i - 1] + num) % m;              s[a[i]]++;          }          LL ans = 0        for(i = 0; i < m; i++)              ans += (LL)s[i] * (s[i] - 1) / 2;            printf("Case %d: %lld\n", ++cnt, ans + s[0]);      }  


下面这个代码其实和上面的一样,但是技巧很棒!(想出这个的人,看到这会疯 ^-^ 哈……)


#include <algorithm>#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>#include <cmath>#include <queue>#include <set>#include <map>#define ll longlongusing namespace std;int a[100005];int main(){    int T, cas = 1;    scanf("%d", &T);    while(T--)    {        memset(a, 0, sizeof(a));        int n, m, sum = 0;        ll ans = 0;        scanf("%d%d", &n, &m);        for(int i = 1; i <= n; i++)        {            int x;            scanf("%d", &x);            sum = (sum + x) % m;            ans +=(ll)a[sum];            a[sum]++;            if(sum == 0)                ans++;        }        printf("Case %d: %lld\n", cas++, ans);    }    return 0;}





原创粉丝点击