P3131 [USACO16JAN] Subsequences Summing to Sevens S

难度:普及−;

题意

​ 数据范围: 1 ≤ N ≤ 50000 1 \le N \le 50000 1N50000 0 ≤ a i ≤ 1000000 0 \le a_i \le 1000000 0ai1000000

​ 给定 n n n 个数,求一段区间和是 7 7 7 的倍数,找出这一段的长度是为多少,如果不存在输出 0 0 0

分析

​ 很快就想到的是前缀和 + 暴力枚举 O ( n 2 ) O(n^2) O(n2),枚举区间的起点和终点 [ l , r ] [l,r] [l,r],并判断区间是否为 7 7 7 的倍数来查找出符合的区间长度作更新,具体代码可以看下面。期望得分是 70 70 70 pts(实际得分 68 68 68 pts,不重要)。

O ( n 2 ) O(n^2) O(n2) 做法:

#include 
#define ll long long

const int N = 50050;
int n, a[N], sum[N];

int main()
{
    std::ios::sync_with_stdio(false), std::cin.tie(nullptr);
    std::cin >> n;
    for (int i = 1; i <= n; i++)
        std::cin >> a[i], sum[i] = sum[i - 1] + a[i];
    int ans = 0;
    for (int l = 1; l <= n; l++)
        for (int r = l; r <= n; r++)
            if (ans > (r - l + 1)) // 比答案的长度小,不可能成为答案
                continue;
            else
            {
                int s = sum[r] - sum[l - 1]; // 区间区间和
                if (s % 7 == 0)
                    ans = std::max(ans, r - l + 1);
            }
    std::cout << ans << "\n";
    return 0;
}

优化

n=7

ai
3
5
1
6
2
14
10

前缀和 - 余数
3 - 3 *
8 - 1
9 - 2
15 - 1
17 - 3
31 - 3 * 
41 - 6

答案序列 [5,1,6,2,14], len = 5

先按照题目的要求,对前缀和作模 7 7 7 的操作,一是加速计算区间和,二是符合题目的区间和要求。通过上述的模拟 / 数学规律得知, a ≡ b ( m o d   c ) a \equiv b( mod \ c) ab(mod c) ( a − b ) ≡ 0 ( m o d   c ) (a-b) \equiv 0 (mod \ c) (ab)0(mod c)

例如: a = 10 , b = 3 , c = 7 a=10,b=3,c=7 a=10,b=3,c=7 时,满足上述规律,证明起来比较简单,这里不再赘述。

具体做法,扫描余数 0 ∼ 6 0 \sim 6 06,再从序列的起点开始扫找第一个等于余数 i i i 的位置 s s s,再从序列的末尾开始扫第一个等于余数 i i i 的位置 t t t,显然 t − s t-s ts但不包含 s s s 本身)的倍数为 7 7 7 的序列,故不需要加一。

​ 下面的代码要注意, j j j 是枚举序列的符合要求的起点和终点,但如果只有一个元素的时候,答案就不正确了被 hack,正确的做法是将 j j j 每次枚举的位置从 0 0 0 开始,因为我们发现如果和整除七的一段是从头开始的,而上面也说过,和为 7 倍数的一段不包括 s,所以就没有办法统计答案从头开始的数据。

​ 怎么办呢?很简单,只需要把内层循环的 j 改成从 0 开始就可以解决从第一位开始的情况。

O ( n ) O(n) O(n),参考代码:

#include 
#define ll long long

const int N = 50010;
int a[N], sum[N], n;

int main()
{
    std::ios::sync_with_stdio(false), std::cin.tie(nullptr);
    std::cin >> n;
    for (int i = 1; i <= n; i++)
        std::cin >> a[i], sum[i] = (sum[i - 1] + a[i]) % 7;
    int ans = 0;
    for (int i = 0; i <= 6; i++) // 遍历余数
    // 想头尾第一个相同的余数
    {
        int l = 0, r = 0;
        for (int j = 0; j <= n; j++) // 不包含 l 本身,即 ( l,r ]
            if (sum[j] == i)
            {
                l = j;
                break;
            }
        for (int j = n; j >= 0; j--)
            if (sum[j] == i)
            {
                r = j;
                break;
            }
        ans = std::max(ans, r - l);
    }
    std::cout << ans << '\n';
    return 0;
}

你可能感兴趣的:(USACO,历年真题题解报告,c++,算法,USACO,Silver)